diff --git a/.ci/Gemfile.travis b/.ci/Gemfile.travis
new file mode 100644
index 00000000..b9c52c2b
--- /dev/null
+++ b/.ci/Gemfile.travis
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+gem 'xcpretty'
+gem 'xcpretty-travis-formatter'
\ No newline at end of file
diff --git a/.ci/Makefile b/.ci/Makefile
new file mode 100644
index 00000000..a920fa04
--- /dev/null
+++ b/.ci/Makefile
@@ -0,0 +1,73 @@
+# Requirements
+# ============
+#
+# Xcode 7+
+
+XCODEBUILD := set -o pipefail && $(shell command -v xcodebuild)
+
+# Xcode Version Information
+XCODEVERSION_FULL := $(word 2, $(shell xcodebuild -version))
+XCODEVERSION_MAJOR := $(shell xcodebuild -version 2>&1 | grep Xcode | cut -d' ' -f2 | cut -d'.' -f1)
+XCODEVERSION_MINOR := $(shell xcodebuild -version 2>&1 | grep Xcode | cut -d' ' -f2 | cut -d'.' -f2)
+
+# The Xcode Version, containing only the "MAJOR.MINOR" (ex. "8.3" for Xcode 8.3, 8.3.1, etc.)
+XCODEVERSION := $(XCODEVERSION_MAJOR).$(XCODEVERSION_MINOR)
+
+# Used to determine if xcpretty is available
+XCPRETTY_PATH := $(shell command -v xcpretty 2> /dev/null)
+
+
+# Targets
+# =======
+#
+# make test_build: build SQLiteLib for a specific destination or sdk
+
+default: test_build
+
+
+# Tests
+# =====
+
+# If xcpretty is available, use it for xcodebuild output
+XCPRETTY =
+ifdef XCPRETTY_PATH
+ XCPRETTY = | xcpretty -c
+
+ # On Travis-CI, use xcpretty-travis-formatter
+ ifeq ($(TRAVIS),true)
+ XCPRETTY += -f `xcpretty-travis-formatter`
+ endif
+endif
+
+# Test if SQLiteLib can be built for a specified destination
+test_build: clean
+ifneq ($(DESTINATION),)
+
+ cd .. && \
+ cp SQLiteLib-USER.xcconfig.example SQLiteLib-USER.xcconfig && \
+ $(XCODEBUILD) \
+ -project SQLiteLib.xcodeproj \
+ -scheme sqlitelib \
+ -configuration Release \
+ -destination '$(DESTINATION)' \
+ clean build \
+ $(XCPRETTY)
+
+else
+ifeq ($(SDK),)
+ $(error a DESTINATION or SDK must be specified (see xcodebuild documentation))
+endif
+ cd .. && \
+ cp SQLiteLib-USER.xcconfig.example SQLiteLib-USER.xcconfig && \
+ $(XCODEBUILD) \
+ -project SQLiteLib.xcodeproj \
+ -scheme sqlitelib \
+ -configuration Release \
+ -sdk '$(SDK)' \
+ clean build \
+ $(XCPRETTY)
+endif
+
+clean:
+ cd .. && \
+ rm -f sqlite3.h
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..ba497936
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,198 @@
+language: objective-c
+xcode_project: SQLiteLib.xcodeproj
+
+# Disable the default Travis-CI submodule logic
+git:
+ submodules: false
+
+## Travis-CI doesn’t currently support automatically expanding osx_image
+## into the build matrix.
+##
+## see: https://github.com/travis-ci/travis-ci/issues/7587
+##
+#
+#gemfile: .ci/Gemfile.travis
+#
+#osx_image:
+# - xcode7.3
+# - xcode8.3
+#
+#env:
+# - DESTINATION="platform=macOS,arch=x86_64"
+# - DESTINATION="platform=macOS,arch=i386"
+# - DESTINATION="generic/platform=iOS"
+# - DESTINATION="generic/platform=watchOS"
+# - DESTINATION="generic/platform=tvOS"
+# - DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.3.1"
+
+matrix:
+ include:
+
+ ###################################
+ ### Xcode 7.3 on macOS 10.11
+
+ - os: osx
+ osx_image: xcode7.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=OS X"
+ - os: osx
+ osx_image: xcode7.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=iOS"
+ - os: osx
+ osx_image: xcode7.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=watchOS"
+ - os: osx
+ osx_image: xcode7.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=tvOS"
+ - os: osx
+ osx_image: xcode7.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=9.3"
+
+ ###################################
+ ### Xcode 8.3.3 on macOS 10.12
+
+ - os: osx
+ osx_image: xcode8.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=macOS"
+ - os: osx
+ osx_image: xcode8.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=iOS"
+ - os: osx
+ osx_image: xcode8.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=watchOS"
+ - os: osx
+ osx_image: xcode8.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=tvOS"
+ - os: osx
+ osx_image: xcode8.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=iOS Simulator,name=iPhone 6s,OS=10.3.1"
+ - os: osx
+ osx_image: xcode8.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - SDK="iphoneos"
+
+ ###################################
+ ### Xcode 9.4.1 on macOS 10.13
+
+ - os: osx
+ osx_image: xcode9.4
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=macOS"
+ - os: osx
+ osx_image: xcode9.4
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=iOS"
+ - os: osx
+ osx_image: xcode9.4
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=watchOS"
+ - os: osx
+ osx_image: xcode9.4
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=tvOS"
+ - os: osx
+ osx_image: xcode9.4
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=iOS Simulator,name=iPhone 8,OS=11.4"
+ - os: osx
+ osx_image: xcode9.4
+ gemfile: .ci/Gemfile.travis
+ env:
+ - SDK="iphoneos"
+
+ ###################################
+ ### Xcode 10.3 on macOS 10.14
+
+ - os: osx
+ osx_image: xcode10.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=macOS"
+ - os: osx
+ osx_image: xcode10.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=iOS"
+ - os: osx
+ osx_image: xcode10.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=watchOS"
+ - os: osx
+ osx_image: xcode10.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=tvOS"
+ - os: osx
+ osx_image: xcode10.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=iOS Simulator,name=iPhone 8,OS=12.4"
+ - os: osx
+ osx_image: xcode10.3
+ gemfile: .ci/Gemfile.travis
+ env:
+ - SDK="iphoneos"
+
+ ###################################
+ ### Xcode 11.2.1 on macOS 10.14
+
+ - os: osx
+ osx_image: xcode11.2
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=macOS"
+ - os: osx
+ osx_image: xcode11.2
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=iOS"
+ - os: osx
+ osx_image: xcode11.2
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=watchOS"
+ - os: osx
+ osx_image: xcode11.2
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="generic/platform=tvOS"
+ - os: osx
+ osx_image: xcode11.2
+ gemfile: .ci/Gemfile.travis
+ env:
+ - DESTINATION="platform=iOS Simulator,name=iPhone 8,OS=13.2.2"
+ - os: osx
+ osx_image: xcode11.2
+ gemfile: .ci/Gemfile.travis
+ env:
+ - SDK="iphoneos"
+
+script:
+ - cd .ci
+ - make test_build
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..3a40730f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+test: test_open_connection
+
+# Test that we can use the custom SQLite library and open a connection.
+test_open_connection: build/open_connection
+ build/open_connection
+
+build/open_connection: sqlite3.h
+ cc test/open_connection.c -Lbuild/Release -lsqlitecustom -I. -o build/open_connection
+
+sqlite3.h: SQLiteLib-USER.xcconfig
+ xcodebuild -project SQLiteLib.xcodeproj -target sqlitecustom -sdk macosx
+
+SQLiteLib-USER.xcconfig: SQLiteLib-USER.xcconfig.example
+ cp -n SQLiteLib-USER.xcconfig.example SQLiteLib-USER.xcconfig
+
+distclean:
+ git clean -dfx .
+
+.PHONY: test test_open_connection distclean
diff --git a/README.md b/README.md
index a361c7d4..85239de6 100755
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
SQLiteLib `.xcodeproj`
==========
- 
+  [](https://travis-ci.org/swiftlyfalling/SQLiteLib)
An Xcode project to easily build a custom SQLite static library for use in OSX and iOS frameworks and apps.
@@ -8,11 +8,11 @@ If you need a specific version of SQLite, or specific SQLite compilation options
-**September 10, 2016: Update SQLite to 3.14.1** ([changelog](CHANGELOG.md)).
+**January 2, 2025: Update SQLite to 3.47.2** ([changelog](https://github.com/swiftlyfalling/SQLiteLib/commits/master)).
**Requirements**: iOS 8.0+ / OSX 10.9+, Xcode 7.3+
-**SQLite Included:** 3.14.1
+**SQLite Included:** **[3.47.2](https://www.sqlite.org/releaselog/3_47_2.html)**
@@ -39,7 +39,7 @@ For example: ([GRDB.swift](https://github.com/groue/GRDB.swift)).
## Customization:
-By default, SQLiteLib builds SQLite with options that match the built-in system version of SQLite on OSX and iOS (as of OSX 10.11.6, iOS 9.3.5), [with one exception*](#additional-details).
+By default, SQLiteLib builds SQLite with options that match the built-in system version of SQLite on OSX and iOS (as of OSX 10.11.6, iOS 9.3.5), [with two exception*](#additional-details).
#### Specifying Additional SQLite Compilation Options:
@@ -65,7 +65,7 @@ There is no need to modify any other files.
#### Compiling a Specific Version of SQLite:
-SQLiteLib currently ships with the source for SQLite 3.14.1.
+SQLiteLib currently ships with the source for a specific version of SQLite (see above).
If you'd like to compile a newer (or older) version, the process is simple:
@@ -81,7 +81,7 @@ If you'd like to compile a newer (or older) version, the process is simple:
> > The versions of the SQLite amalgamation that are supplied on the download page are normally adequate for most users. However, some projects may want or need to build their own amalgamations. A common reason for building a custom amalgamation is in order to use certain compile-time options to customize the SQLite library. Recall that the SQLite amalgamation contains a lot of C-code that is generated by auxiliary programs and scripts. Many of the compile-time options effect this generated code and **must be supplied to the code generators before the amalgamation is assembled**.
-####**Quick Guide to Using the Latest version of SQLite**:
+#### **Quick Guide to Using the Latest version of SQLite**:
The snapshop of the complete (raw) source tree for the *current* version of SQLite is available on the ([SQLite Download Page](https://www.sqlite.org/download.html#old)) under: **Alternative Source Code Formats**.
You'll want the file named "sqlite-src-*version*.zip".
@@ -125,24 +125,24 @@ The built-in OSX/iOS version of SQLite were built with the following compilation
- `SYSTEM_MALLOC`
- `THREADSAFE=2`
-SQLiteLib uses these settings with one exception - on iOS:
+These settings are reused, with two exceptions.
-The SQLite code (verified in: 3.14.1) uses a deprecated function (`gethostuuid()`).
+- SQLiteLib does not use `OMIT_LOAD_EXTENSION`, allowing applications to load SQLite extensions.
+- SQLiteLib uses `SQLITE_ENABLE_LOCKING_STYLE=1` on OSX, **but on iOS, SQLiteLib compiles with `ENABLE_LOCKING_STYLE=0`**.
-D. Richard Hipp (SQLite architect), suggests working around this on iOS using `-DSQLITE_ENABLE_LOCKING_STYLE=0`:
-> "The SQLITE_ENABLE_LOCKING_STYLE thing is an apple-only extension that
-> boosts performance when SQLite is used on a network filesystem. This
-> is important on MacOS because some users think it is a good idea to
-> put their home directory on a network filesystem.
->
-> I'm guessing this is not really a factor on iOS."
+ The reason for this is that the SQLite code uses a deprecated function (`gethostuuid()`).
-Thus, SQLiteLib uses `SQLITE_ENABLE_LOCKING_STYLE=1` on OSX,
-**but on iOS, SQLiteLib compiles with `ENABLE_LOCKING_STYLE=0`**.
+ D. Richard Hipp (SQLite architect), suggests working around this on iOS using `-DSQLITE_ENABLE_LOCKING_STYLE=0`:
+ > "The SQLITE_ENABLE_LOCKING_STYLE thing is an apple-only extension that
+ > boosts performance when SQLite is used on a network filesystem. This
+ > is important on MacOS because some users think it is a good idea to
+ > put their home directory on a network filesystem.
+ >
+ > I'm guessing this is not really a factor on iOS."
-This removes the code that uses the deprecated function, but doesn't get rid of the warning that "`gethostuuid() is disabled`" (as of 3.14.1).
+ This removes the code that uses the deprecated function, but doesn't get rid of the warning that "`gethostuuid() is disabled`".
-To prevent this warning, SQLiteLib separately specifies `-Wno-#warnings` when building for iOS.
+ To prevent this warning, SQLiteLib separately specifies `-Wno-#warnings` when building for iOS.
All of these base settings are configured in the SQLiteLib.xcconfig file.
It is strongly recommended that you do not edit this file. If you'd like to specify additional compilation options, see [the instructions above](#specifying-additional-sqlite-compilation-options).
@@ -161,6 +161,20 @@ The generated SQLite amalgamation files are copied to:
+#### Using the SQLite Encryption Extension
+
+You can include the [SQLite EncryptionExtension](https://www.sqlite.org/see/) if you have a license to do so.
+
+To include it in the generated amalgamation, locate the SEE files on your system. In your `SQLiteLib-USER.xcconfig` file, add the following entries:
+
+```SQLITE_SEE_PREFIX = /path/to/your/see-code/see-prefix.txt
+SQLITE_SEE_CODE = /path/to/your/see-code/see.c
+```
+
+These will then be merged into the generated amalgamation
+
+
+
## Notes:
##### "sqlite3.c" shows as red/missing in Xcode:
diff --git a/SQLiteLib.xcconfig b/SQLiteLib.xcconfig
index c648afe2..8171c48d 100755
--- a/SQLiteLib.xcconfig
+++ b/SQLiteLib.xcconfig
@@ -75,7 +75,7 @@ SUPPORTED_PLATFORMS = iphonesimulator macosx iphoneos appletvsimulator appletvos
// The common set of compilation options (on OSX and iOS).
// NOTE: To add more compilation options, see SQLiteLib-USER.xcconfig.
-SQLLIBRARY_BASE_CFLAGS = -DSQLITE_ENABLE_API_ARMOR -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT -DSQLITE_OMIT_AUTORESET -DSQLITE_OMIT_BUILTIN_TEST -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_SYSTEM_MALLOC -DSQLITE_THREADSAFE=2 -DSQLITE_OS_UNIX=1 $(CUSTOM_SQLLIBRARY_CFLAGS)
+SQLLIBRARY_BASE_CFLAGS = -DSQLITE_ENABLE_API_ARMOR -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT -DSQLITE_OMIT_AUTORESET -DSQLITE_OMIT_BUILTIN_TEST -DSQLITE_SYSTEM_MALLOC -DSQLITE_THREADSAFE=2 -DSQLITE_OS_UNIX=1 $(CUSTOM_SQLLIBRARY_CFLAGS)
SQLLIBRARY_OSX_ADDITIONAL_CFLAGS = -DSQLITE_ENABLE_LOCKING_STYLE=1
SQLLIBRARY_IOS_ADDITIONAL_CFLAGS = -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_MAX_MMAP_SIZE=0
diff --git a/SQLiteLib.xcodeproj/project.pbxproj b/SQLiteLib.xcodeproj/project.pbxproj
index af2bcffb..a56266ed 100755
--- a/SQLiteLib.xcodeproj/project.pbxproj
+++ b/SQLiteLib.xcodeproj/project.pbxproj
@@ -12,7 +12,7 @@
buildConfigurationList = F3BA81441CFB4D5B003DC1BA /* Build configuration list for PBXAggregateTarget "amalgamation" */;
buildPhases = (
EEDD06761CFE1FD300478EC5 /* Copy .example configuration, if no USER configuration present */,
- F3BA81451CFB4D7B003DC1BA /* ShellScript */,
+ F3BA81451CFB4D7B003DC1BA /* Build amalgamation */,
);
dependencies = (
);
@@ -22,7 +22,7 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
- F32EB7D41CFBD08A007C7948 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = F32EB7D31CFBD08A007C7948 /* sqlite3.c */; };
+ EE751A3F1D9D774E00A88200 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = EE751A3E1D9D774E00A88200 /* sqlite3.c */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -36,9 +36,11 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ B538715222566E13004BFBB9 /* copyExampleConfiguration.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = copyExampleConfiguration.sh; path = scripts/copyExampleConfiguration.sh; sourceTree = ""; };
+ B538715322566F25004BFBB9 /* buildAmalgamation.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = buildAmalgamation.sh; path = scripts/buildAmalgamation.sh; sourceTree = ""; };
D2AAC046055464E500DB518D /* libsqlitecustom.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsqlitecustom.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ EE751A3E1D9D774E00A88200 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sqlite3.c; path = ../sqlite/sqlite3.c; sourceTree = BUILT_PRODUCTS_DIR; };
F32EB7781CFBC91D007C7948 /* sqlite3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sqlite3.h; sourceTree = ""; };
- F32EB7D31CFBD08A007C7948 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqlite3.c; sourceTree = BUILT_PRODUCTS_DIR; };
F3F14A621CF9E59E00E8D86B /* SQLiteLib.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SQLiteLib.xcconfig; sourceTree = ""; };
F3F14A6D1CF9E9DB00E8D86B /* SQLiteLib-USER.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "SQLiteLib-USER.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
@@ -59,6 +61,8 @@
children = (
F3F14A6D1CF9E9DB00E8D86B /* SQLiteLib-USER.xcconfig */,
F3F14A621CF9E59E00E8D86B /* SQLiteLib.xcconfig */,
+ B538715222566E13004BFBB9 /* copyExampleConfiguration.sh */,
+ B538715322566F25004BFBB9 /* buildAmalgamation.sh */,
08FB7795FE84155DC02AAC07 /* Source */,
C6A0FF2B0290797F04C91782 /* Documentation */,
1AB674ADFE9D54B511CA2CBB /* Products */,
@@ -69,7 +73,7 @@
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
- F32EB7D31CFBD08A007C7948 /* sqlite3.c */,
+ EE751A3E1D9D774E00A88200 /* sqlite3.c */,
F32EB7781CFBC91D007C7948 /* sqlite3.h */,
);
name = Source;
@@ -127,7 +131,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0730;
+ LastUpgradeCheck = 1320;
TargetAttributes = {
F3BA81411CFB4D5B003DC1BA = {
CreatedOnToolsVersion = 7.3.1;
@@ -136,13 +140,14 @@
};
buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "SQLiteLib" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 1;
knownRegions = (
- English,
- Japanese,
- French,
- German,
+ fr,
+ de,
+ ja,
+ Base,
+ en,
);
mainGroup = 08FB7794FE84155DC02AAC07 /* sqlcipher */;
projectDirPath = "";
@@ -165,13 +170,14 @@
);
name = "Copy .example configuration, if no USER configuration present";
outputPaths = (
+ "$(SRCROOT)/SQLiteLib-USER.xcconfig",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "copiedfiles=false\nfor ((i=0; i < SCRIPT_INPUT_FILE_COUNT ; i++))\ndo\n INFILE=SCRIPT_INPUT_FILE_$i\n EXAMPLEFILE=${!INFILE};\n OUTPUTFILE=${EXAMPLEFILE%.example}\n echo \"${INFILE}=${EXAMPLEFILE}\"\n if [ -f \"$EXAMPLEFILE\" ]\n then\n if [ ! -f \"$OUTPUTFILE\" ];\n then\n echo \"Expected USER configuration file doesn't exist\"\n echo \"Copying .example file\"\n cp $EXAMPLEFILE $OUTPUTFILE\n touch $OUTPUTFILE\n copiedfiles=true\n else\n echo \"USER Configuration file already exists: ${OUTPUTFILE}\"\n echo \"Skipping .example copy.\"\n fi\n else\n echo \"Missing input .example file: ${EXAMPLEFILE}\"\n exit 1\n fi\ndone\n\nif [ \"$copiedfiles\" = true ] ; then\n touch \"${SRCROOT}/SQLiteLib.xcconfig\"\n sleep 1\nfi\n";
+ shellScript = "$SRCROOT/scripts/copyExampleConfiguration.sh\n";
showEnvVarsInLog = 0;
};
- F3BA81451CFB4D7B003DC1BA /* ShellScript */ = {
+ F3BA81451CFB4D7B003DC1BA /* Build amalgamation */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -181,13 +187,14 @@
"$(SRCROOT)/SQLiteLib.xcconfig",
"$(SRCROOT)/SQLiteLib-Custom.xcconfig",
);
+ name = "Build amalgamation";
outputPaths = (
- "${BUILT_PRODUCTS_DIR}/sqlite3.c",
+ "${BUILD_DIR}/sqlite/sqlite3.c",
"${PROJECT_DIR}/sqlite3.h",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -e\n\necho \"Configure and generate SQLite amalgamation files\"\n\npattern=\" |'\"\nif [[ ${PROJECT_DIR} =~ $pattern ]]\nthen\n echo \"Unsafe PROJECT_DIR for make - contains spaces.\"\n echo \"PROJECT_DIR=${PROJECT_DIR}\"\n exit 1\nfi\n\nSDK_VERSION=$(echo ${SDK_NAME} | grep -o '.\\{3\\}$')\n\n# Configure and make the amalgamation files using the appropriate simulator SDK\nif [ ${PLATFORM_NAME} = \"macosx\" ]; then\nSDK_PLATFORM_NAME=\"macosx\"\nelif [ ${PLATFORM_NAME} = \"appletvsimulator\" ] || [ ${PLATFORM_NAME} = \"appletvos\" ]; then\nSDK_PLATFORM_NAME=\"appletvsimulator\"\nelif [ ${PLATFORM_NAME} = \"watchsimulator\" ] || [ ${PLATFORM_NAME} = \"watchos\" ]; then\nSDK_PLATFORM_NAME=\"appletvsimulator\" #watchsimulator sdk doesn't work for build?\nelif [ ${PLATFORM_NAME} = \"iphonesimulator\" ] || [ ${PLATFORM_NAME} = \"iphoneos\" ]; then\nSDK_PLATFORM_NAME=\"iphonesimulator\"\nelse\n echo \"Unsupported target platform and SDK: ${PLATFORM_NAME}, ${SDK_NAME}\"\n exit 1\nfi\n\necho \"XCode has selected SDK: ${PLATFORM_NAME} with version: ${SDK_VERSION}\"\necho \"...therefore, make amalgamation source with SDK_PLATFORM_NAME = ${SDK_PLATFORM_NAME}\"\n\nSDKROOT=\"$(xcrun --sdk $SDK_PLATFORM_NAME --show-sdk-path)\"\nCC=\"$(xcrun --sdk $SDK_PLATFORM_NAME -f clang)\"\nCXX=\"$(xcrun --sdk $SDK_PLATFORM_NAME -f clang++)\"\nCFLAGS=\"-isysroot $SDKROOT $OTHER_CFLAGS\"\nCXXFLAGS=$CFLAGS\nexport CC CXX CFLAGS CXXFLAGS\n\nmkdir -p \"${DERIVED_SOURCES_DIR}\"\ncd \"${DERIVED_SOURCES_DIR}\"\n\necho \"Clean-up previous files if necessary\"\n[ -e Makefile ] && make distclean\n\necho \"Configure makefile\"\n\"${PROJECT_DIR}/sqlite/configure\" --srcdir=\"${PROJECT_DIR}/sqlite/\"\n\necho \"Make SQLite amalgamation\"\nmake sqlite3.c\n\n# copy generated amalgamation files (sqlite3.c and sqlite3.h)\n# to BUILT_PRODUCTS_DIR\nmkdir -p \"${BUILT_PRODUCTS_DIR}\"\necho \"Copying sqlite3.c to ${BUILT_PRODUCTS_DIR}/sqlite3.c\"\ncp ./sqlite3.c \"${BUILT_PRODUCTS_DIR}/sqlite3.c\"\necho \"Copying sqlite3.h to ${PROJECT_DIR}/sqlite3.h\"\ncp ./sqlite3.h \"${PROJECT_DIR}/sqlite3.h\"\n\necho \"Finished generating SQLite amalgamation files\"\n";
+ shellScript = "$SRCROOT/scripts/buildamalgamation.sh\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -196,7 +203,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F32EB7D41CFBD08A007C7948 /* sqlite3.c in Sources */,
+ EE751A3F1D9D774E00A88200 /* sqlite3.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -216,6 +223,7 @@
baseConfigurationReference = F3F14A621CF9E59E00E8D86B /* SQLiteLib.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_WEAK = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
@@ -237,6 +245,7 @@
baseConfigurationReference = F3F14A621CF9E59E00E8D86B /* SQLiteLib.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_WEAK = YES;
COMBINE_HIDPI_IMAGES = YES;
"GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64]" = NO;
HEADER_SEARCH_PATHS = (
@@ -253,39 +262,79 @@
1DEB91F008733DB70010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)";
- "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)";
- "ARCHS[sdk=macosx*]" = (
- x86_64,
- i386,
- );
+ ARCHS = "$(ARCHS_STANDARD)";
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
- VALID_ARCHS = "arm64 armv7 armv7s x86_64 i386";
+ VALID_ARCHS = "$(ARCHS_STANDARD)";
};
name = Debug;
};
1DEB91F108733DB70010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)";
- "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)";
- "ARCHS[sdk=macosx*]" = (
- x86_64,
- i386,
- );
+ ARCHS = "$(ARCHS_STANDARD)";
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
- VALID_ARCHS = "arm64 armv7 armv7s x86_64 i386";
+ VALID_ARCHS = "$(ARCHS_STANDARD)";
};
name = Release;
};
@@ -293,7 +342,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F3F14A621CF9E59E00E8D86B /* SQLiteLib.xcconfig */;
buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ SUPPORTED_PLATFORMS = macosx;
};
name = Debug;
};
@@ -301,8 +353,11 @@
isa = XCBuildConfiguration;
baseConfigurationReference = F3F14A621CF9E59E00E8D86B /* SQLiteLib.xcconfig */;
buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
GCC_OPTIMIZATION_LEVEL = s;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx;
+ SUPPORTED_PLATFORMS = macosx;
};
name = Release;
};
diff --git a/SQLiteLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SQLiteLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/SQLiteLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/SQLiteLib.xcodeproj/xcshareddata/xcschemes/sqlitelib.xcscheme b/SQLiteLib.xcodeproj/xcshareddata/xcschemes/sqlitelib.xcscheme
index da8d0fbf..3d739216 100644
--- a/SQLiteLib.xcodeproj/xcshareddata/xcschemes/sqlitelib.xcscheme
+++ b/SQLiteLib.xcodeproj/xcshareddata/xcschemes/sqlitelib.xcscheme
@@ -1,6 +1,6 @@
-
-
-
- "${DERIVED_SOURCES_DIR}/sqlite3-see.c"
+fi
+
+# copy generated amalgamation files (sqlite3.c and sqlite3.h)
+# to BUILD_DIR/sqlite
+# Overwrite with SQLite-SEE if present
+SQLITE_OUTPUT_DIR="${BUILD_DIR}/sqlite"
+mkdir -p "${SQLITE_OUTPUT_DIR}"
+echo "Copying sqlite3.c to ${SQLITE_OUTPUT_DIR}/sqlite3.c"
+cp ./sqlite3.c "${SQLITE_OUTPUT_DIR}/sqlite3.c"
+echo "Copying sqlite3.h to ${PROJECT_DIR}/sqlite3.h"
+cp ./sqlite3.h "${PROJECT_DIR}/sqlite3.h"
+if [ "$hasSEE" = true ] ;
+ then
+ echo "Overwriting sqlite3.c to with sqlite3-see.c"
+ cp ./sqlite3-see.c "${SQLITE_OUTPUT_DIR}/sqlite3.c"
+fi
+
+echo "Finished generating SQLite amalgamation files"
+
diff --git a/scripts/copyExampleConfiguration.sh b/scripts/copyExampleConfiguration.sh
new file mode 100755
index 00000000..1861bfc4
--- /dev/null
+++ b/scripts/copyExampleConfiguration.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Copies example .xcconfig file if no user configuration is present
+
+copiedfiles=false
+for ((i=0; i < SCRIPT_INPUT_FILE_COUNT ; i++))
+ do
+ INFILE=SCRIPT_INPUT_FILE_$i
+ EXAMPLEFILE=${!INFILE};
+ OUTPUTFILE=${EXAMPLEFILE%.example}
+ echo "${INFILE}=${EXAMPLEFILE}"
+ if [ -f "$EXAMPLEFILE" ]
+ then
+ if [ ! -f "$OUTPUTFILE" ];
+ then
+ echo "Expected USER configuration file doesn't exist"
+ echo "Copying .example file"
+ cp $EXAMPLEFILE $OUTPUTFILE
+ touch $OUTPUTFILE
+ copiedfiles=true
+ else
+ echo "USER Configuration file already exists: ${OUTPUTFILE}"
+ echo "Skipping .example copy."
+ fi
+ else
+ echo "Missing input .example file: ${EXAMPLEFILE}"
+ exit 1
+ fi
+done
+
+if [ "$copiedfiles" = true ] ; then
+ touch "${SRCROOT}/SQLiteLib.xcconfig"
+ sleep 1
+fi
diff --git a/sqlite/.fossil-settings/empty-dirs b/sqlite/.fossil-settings/empty-dirs
new file mode 100644
index 00000000..64fb6839
--- /dev/null
+++ b/sqlite/.fossil-settings/empty-dirs
@@ -0,0 +1 @@
+compat
diff --git a/sqlite/.fossil-settings/ignore-glob b/sqlite/.fossil-settings/ignore-glob
new file mode 100644
index 00000000..5282ca9c
--- /dev/null
+++ b/sqlite/.fossil-settings/ignore-glob
@@ -0,0 +1 @@
+compat/*
diff --git a/sqlite/LICENSE.md b/sqlite/LICENSE.md
new file mode 100644
index 00000000..f68a6c17
--- /dev/null
+++ b/sqlite/LICENSE.md
@@ -0,0 +1,6 @@
+The author disclaims copyright to this source code. In place of
+a legal notice, here is a blessing:
+
+ * May you do good and not evil.
+ * May you find forgiveness for yourself and forgive others.
+ * May you share freely, never taking more than you give.
diff --git a/sqlite/Makefile.in b/sqlite/Makefile.in
index 9b2fe86c..a8359e2a 100644
--- a/sqlite/Makefile.in
+++ b/sqlite/Makefile.in
@@ -22,7 +22,7 @@ TOP = @abs_srcdir@
#
BCC = @BUILD_CC@ @BUILD_CFLAGS@
-# TCC is the C Compile and options for use in building executables that
+# TCC is the C Compile and options for use in building executables that
# will run on the target platform. (BCC and TCC are usually the
# same unless your are cross-compiling.) Separate CC and CFLAGS macros
# are provide so that these aspects of the build process can be changed
@@ -32,10 +32,11 @@ CC = @CC@
CFLAGS = @CPPFLAGS@ @CFLAGS@
TCC = ${CC} ${CFLAGS} -I. -I${TOP}/src -I${TOP}/ext/rtree -I${TOP}/ext/icu
TCC += -I${TOP}/ext/fts3 -I${TOP}/ext/async -I${TOP}/ext/session
+TCC += -I${TOP}/ext/userauth
# Define this for the autoconf-based build, so that the code knows it can
-# include the generated config.h
-#
+# include the generated sqlite_cfg.h
+#
TCC += -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
@@ -56,6 +57,7 @@ LIBTCL = @TCL_LIB_SPEC@
#
READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
READLINE_FLAGS += -DHAVE_EDITLINE=@TARGET_HAVE_EDITLINE@
+READLINE_FLAGS += -DHAVE_LINENOISE=@TARGET_HAVE_LINENOISE@
# The library that programs using readline() must link against.
#
@@ -66,7 +68,7 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
TCC += -DSQLITE_THREADSAFE=@SQLITE_THREADSAFE@
# Any target libraries which libsqlite must be linked against
-#
+#
TLIBS = @LIBS@ $(LIBS)
# Flags controlling use of the in memory btree implementation
@@ -78,17 +80,24 @@ TLIBS = @LIBS@ $(LIBS)
TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@
# Enable/disable loadable extensions, and other optional features
-# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
-# The same set of OMIT and ENABLE flags should be passed to the
+# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
+# The same set of OMIT and ENABLE flags should be passed to the
# LEMON parser generator and the mkkeywordhash tool as well.
-OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
+#
+# Add OPTIONS=... on the command line to append additional options
+# to the OPT_FEATURE_FLAGS.
+#
+OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ $(OPTIONS)
-TCC += $(OPT_FEATURE_FLAGS)
+TCC += $(OPT_FEATURE_FLAGS)
# Add in any optional parameters specified on the make commane line
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
TCC += $(OPTS)
+# Add in compile-time options for some libraries used by extensions
+TCC += @HAVE_ZLIB@
+
# Version numbers and release number for the SQLite being compiled.
#
VERSION = @VERSION@
@@ -112,19 +121,20 @@ HAVE_TCL = @HAVE_TCL@
#
TCLSH_CMD = @TCLSH_CMD@
-# Where do we want to install the tcl plugin
+# Additional options when running tests using testrunner.tcl
+# This is usually either blank, or else --status
#
-TCLLIBDIR = @TCLLIBDIR@
+TSTRNNR_OPTS = @TSTRNNR_OPTS@
-# The suffix used on shared libraries. Ex: ".dll", ".so", ".dylib"
+# Where do we want to install the tcl plugin
#
-SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
+TCLLIBDIR = @TCLLIBDIR@
# If gcov support was enabled by the configure script, add the appropriate
# flags here. It's not always as easy as just having the user add the right
# CFLAGS / LDFLAGS, because libtool wants to use CFLAGS when linking, which
-# causes build errors with -fprofile-arcs -ftest-coverage with some GCCs.
-# Supposedly GCC does the right thing if you use --coverage, but in
+# causes build errors with -fprofile-arcs -ftest-coverage with some GCCs.
+# Supposedly GCC does the right thing if you use --coverage, but in
# practice it still fails. See:
#
# http://www.mail-archive.com/debian-gcc@lists.debian.org/msg26197.html
@@ -161,12 +171,14 @@ LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL)
###############################################################################
USE_AMALGAMATION = @USE_AMALGAMATION@
+AMALGAMATION_LINE_MACROS = @AMALGAMATION_LINE_MACROS@
# Object files for the SQLite library (non-amalgamation).
#
LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
- callback.lo complete.lo ctime.lo date.lo dbstat.lo delete.lo \
+ callback.lo complete.lo ctime.lo \
+ date.lo dbpage.lo dbstat.lo delete.lo \
expr.lo fault.lo fkey.lo \
fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \
fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \
@@ -174,19 +186,20 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \
fts5.lo \
func.lo global.lo hash.lo \
- icu.lo insert.lo json1.lo legacy.lo loadext.lo \
+ icu.lo insert.lo json.lo legacy.lo loadext.lo \
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
- memjournal.lo \
+ memdb.lo memjournal.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
- notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
+ notify.lo opcodes.lo os.lo os_kv.lo os_unix.lo os_win.lo \
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
random.lo resolve.lo rowset.lo rtree.lo \
- sqlite3session.lo select.lo sqlite3rbu.lo status.lo \
+ sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmt.lo \
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
- update.lo util.lo vacuum.lo \
+ update.lo userauth.lo upsert.lo util.lo vacuum.lo \
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
- vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
- utf.lo vtab.lo
+ vdbetrace.lo vdbevtab.lo \
+ wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
+ window.lo utf.lo vtab.lo
# Object files for the amalgamation.
#
@@ -215,6 +228,7 @@ SRC = \
$(TOP)/src/complete.c \
$(TOP)/src/ctime.c \
$(TOP)/src/date.c \
+ $(TOP)/src/dbpage.c \
$(TOP)/src/dbstat.c \
$(TOP)/src/delete.c \
$(TOP)/src/expr.c \
@@ -226,6 +240,7 @@ SRC = \
$(TOP)/src/hash.h \
$(TOP)/src/hwtime.h \
$(TOP)/src/insert.c \
+ $(TOP)/src/json.c \
$(TOP)/src/legacy.c \
$(TOP)/src/loadext.c \
$(TOP)/src/main.c \
@@ -235,6 +250,7 @@ SRC = \
$(TOP)/src/mem2.c \
$(TOP)/src/mem3.c \
$(TOP)/src/mem5.c \
+ $(TOP)/src/memdb.c \
$(TOP)/src/memjournal.c \
$(TOP)/src/msvc.h \
$(TOP)/src/mutex.c \
@@ -247,6 +263,7 @@ SRC = \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
$(TOP)/src/os_setup.h \
+ $(TOP)/src/os_kv.c \
$(TOP)/src/os_unix.c \
$(TOP)/src/os_win.c \
$(TOP)/src/os_win.h \
@@ -265,7 +282,7 @@ SRC = \
$(TOP)/src/rowset.c \
$(TOP)/src/select.c \
$(TOP)/src/status.c \
- $(TOP)/src/shell.c \
+ $(TOP)/src/shell.c.in \
$(TOP)/src/sqlite.h.in \
$(TOP)/src/sqlite3ext.h \
$(TOP)/src/sqliteInt.h \
@@ -278,6 +295,7 @@ SRC = \
$(TOP)/src/trigger.c \
$(TOP)/src/utf.c \
$(TOP)/src/update.c \
+ $(TOP)/src/upsert.c \
$(TOP)/src/util.c \
$(TOP)/src/vacuum.c \
$(TOP)/src/vdbe.c \
@@ -288,6 +306,7 @@ SRC = \
$(TOP)/src/vdbemem.c \
$(TOP)/src/vdbesort.c \
$(TOP)/src/vdbetrace.c \
+ $(TOP)/src/vdbevtab.c \
$(TOP)/src/vdbeInt.h \
$(TOP)/src/vtab.c \
$(TOP)/src/vxworks.h \
@@ -297,28 +316,11 @@ SRC = \
$(TOP)/src/where.c \
$(TOP)/src/wherecode.c \
$(TOP)/src/whereexpr.c \
- $(TOP)/src/whereInt.h
+ $(TOP)/src/whereInt.h \
+ $(TOP)/src/window.c
# Source code for extensions
#
-SRC += \
- $(TOP)/ext/fts1/fts1.c \
- $(TOP)/ext/fts1/fts1.h \
- $(TOP)/ext/fts1/fts1_hash.c \
- $(TOP)/ext/fts1/fts1_hash.h \
- $(TOP)/ext/fts1/fts1_porter.c \
- $(TOP)/ext/fts1/fts1_tokenizer.h \
- $(TOP)/ext/fts1/fts1_tokenizer1.c
-SRC += \
- $(TOP)/ext/fts2/fts2.c \
- $(TOP)/ext/fts2/fts2.h \
- $(TOP)/ext/fts2/fts2_hash.c \
- $(TOP)/ext/fts2/fts2_hash.h \
- $(TOP)/ext/fts2/fts2_icu.c \
- $(TOP)/ext/fts2/fts2_porter.c \
- $(TOP)/ext/fts2/fts2_tokenizer.h \
- $(TOP)/ext/fts2/fts2_tokenizer.c \
- $(TOP)/ext/fts2/fts2_tokenizer1.c
SRC += \
$(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3.h \
@@ -342,15 +344,19 @@ SRC += \
$(TOP)/ext/icu/icu.c
SRC += \
$(TOP)/ext/rtree/rtree.h \
- $(TOP)/ext/rtree/rtree.c
+ $(TOP)/ext/rtree/rtree.c \
+ $(TOP)/ext/rtree/geopoly.c
SRC += \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/sqlite3session.h
+SRC += \
+ $(TOP)/ext/userauth/userauth.c \
+ $(TOP)/ext/userauth/sqlite3userauth.h
SRC += \
$(TOP)/ext/rbu/sqlite3rbu.h \
$(TOP)/ext/rbu/sqlite3rbu.c
SRC += \
- $(TOP)/ext/misc/json1.c
+ $(TOP)/ext/misc/stmt.c
# Generated source code files
#
@@ -360,7 +366,8 @@ SRC += \
opcodes.h \
parse.c \
parse.h \
- config.h \
+ sqlite_cfg.h \
+ shell.c \
sqlite3.h
# Source code to the test files.
@@ -372,7 +379,6 @@ TESTSRC = \
$(TOP)/src/test4.c \
$(TOP)/src/test5.c \
$(TOP)/src/test6.c \
- $(TOP)/src/test7.c \
$(TOP)/src/test8.c \
$(TOP)/src/test9.c \
$(TOP)/src/test_autoext.c \
@@ -382,6 +388,7 @@ TESTSRC = \
$(TOP)/src/test_blob.c \
$(TOP)/src/test_btree.c \
$(TOP)/src/test_config.c \
+ $(TOP)/src/test_delete.c \
$(TOP)/src/test_demovfs.c \
$(TOP)/src/test_devsym.c \
$(TOP)/src/test_fs.c \
@@ -391,6 +398,7 @@ TESTSRC = \
$(TOP)/src/test_intarray.c \
$(TOP)/src/test_journal.c \
$(TOP)/src/test_malloc.c \
+ $(TOP)/src/test_md5.c \
$(TOP)/src/test_multiplex.c \
$(TOP)/src/test_mutex.c \
$(TOP)/src/test_onefile.c \
@@ -399,40 +407,65 @@ TESTSRC = \
$(TOP)/src/test_quota.c \
$(TOP)/src/test_rtree.c \
$(TOP)/src/test_schema.c \
- $(TOP)/src/test_server.c \
$(TOP)/src/test_superlock.c \
$(TOP)/src/test_syscall.c \
+ $(TOP)/src/test_tclsh.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
+ $(TOP)/src/test_vdbecov.c \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_windirent.c \
+ $(TOP)/src/test_window.c \
$(TOP)/src/test_wsd.c \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/session/test_session.c \
- $(TOP)/ext/rbu/test_rbu.c
+ $(TOP)/ext/recover/sqlite3recover.c \
+ $(TOP)/ext/recover/dbdata.c \
+ $(TOP)/ext/recover/test_recover.c \
+ $(TOP)/ext/intck/test_intck.c \
+ $(TOP)/ext/intck/sqlite3intck.c \
+ $(TOP)/ext/rbu/test_rbu.c
# Statically linked extensions
#
TESTSRC += \
+ $(TOP)/ext/expert/sqlite3expert.c \
+ $(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/misc/amatch.c \
+ $(TOP)/ext/misc/appendvfs.c \
+ $(TOP)/ext/misc/basexx.c \
$(TOP)/ext/misc/carray.c \
+ $(TOP)/ext/misc/cksumvfs.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/csv.c \
+ $(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/eval.c \
+ $(TOP)/ext/misc/explain.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/fuzzer.c \
$(TOP)/ext/fts5/fts5_tcl.c \
$(TOP)/ext/fts5/fts5_test_mi.c \
$(TOP)/ext/fts5/fts5_test_tok.c \
$(TOP)/ext/misc/ieee754.c \
+ $(TOP)/ext/misc/mmapwarm.c \
$(TOP)/ext/misc/nextchar.c \
+ $(TOP)/ext/misc/normalize.c \
$(TOP)/ext/misc/percentile.c \
+ $(TOP)/ext/misc/prefixes.c \
+ $(TOP)/ext/misc/qpvtab.c \
+ $(TOP)/ext/misc/randomjson.c \
$(TOP)/ext/misc/regexp.c \
+ $(TOP)/ext/misc/remember.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/spellfix.c \
+ $(TOP)/ext/misc/stmtrand.c \
$(TOP)/ext/misc/totype.c \
- $(TOP)/ext/misc/wholenumber.c
+ $(TOP)/ext/misc/unionvtab.c \
+ $(TOP)/ext/misc/wholenumber.c \
+ $(TOP)/ext/misc/zipfile.c \
+ $(TOP)/ext/userauth/userauth.c \
+ $(TOP)/ext/rtree/test_rtreedoc.c
# Source code to the library files needed by the test fixture
#
@@ -444,14 +477,17 @@ TESTSRC2 = \
$(TOP)/src/build.c \
$(TOP)/src/ctime.c \
$(TOP)/src/date.c \
+ $(TOP)/src/dbpage.c \
$(TOP)/src/dbstat.c \
$(TOP)/src/expr.c \
$(TOP)/src/func.c \
+ $(TOP)/src/global.c \
$(TOP)/src/insert.c \
$(TOP)/src/wal.c \
$(TOP)/src/main.c \
$(TOP)/src/mem5.c \
$(TOP)/src/os.c \
+ $(TOP)/src/os_kv.c \
$(TOP)/src/os_unix.c \
$(TOP)/src/os_win.c \
$(TOP)/src/pager.c \
@@ -463,6 +499,7 @@ TESTSRC2 = \
$(TOP)/src/pcache1.c \
$(TOP)/src/select.c \
$(TOP)/src/tokenize.c \
+ $(TOP)/src/treeview.c \
$(TOP)/src/utf.c \
$(TOP)/src/util.c \
$(TOP)/src/vdbeapi.c \
@@ -470,9 +507,11 @@ TESTSRC2 = \
$(TOP)/src/vdbe.c \
$(TOP)/src/vdbemem.c \
$(TOP)/src/vdbetrace.c \
+ $(TOP)/src/vdbevtab.c \
$(TOP)/src/where.c \
$(TOP)/src/wherecode.c \
$(TOP)/src/whereexpr.c \
+ $(TOP)/src/window.c \
parse.c \
$(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3_aux.c \
@@ -481,7 +520,9 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/async/sqlite3async.c \
- $(TOP)/ext/session/sqlite3session.c
+ $(TOP)/ext/session/sqlite3session.c \
+ $(TOP)/ext/misc/stmt.c \
+ fts5.c
# Header files used by all library source files.
#
@@ -510,29 +551,24 @@ HDR = \
$(TOP)/src/vdbeInt.h \
$(TOP)/src/vxworks.h \
$(TOP)/src/whereInt.h \
- config.h
+ sqlite_cfg.h
# Header files used by extensions
#
-EXTHDR += \
- $(TOP)/ext/fts1/fts1.h \
- $(TOP)/ext/fts1/fts1_hash.h \
- $(TOP)/ext/fts1/fts1_tokenizer.h
-EXTHDR += \
- $(TOP)/ext/fts2/fts2.h \
- $(TOP)/ext/fts2/fts2_hash.h \
- $(TOP)/ext/fts2/fts2_tokenizer.h
EXTHDR += \
$(TOP)/ext/fts3/fts3.h \
$(TOP)/ext/fts3/fts3Int.h \
$(TOP)/ext/fts3/fts3_hash.h \
$(TOP)/ext/fts3/fts3_tokenizer.h
EXTHDR += \
- $(TOP)/ext/rtree/rtree.h
+ $(TOP)/ext/rtree/rtree.h \
+ $(TOP)/ext/rtree/geopoly.c
EXTHDR += \
$(TOP)/ext/icu/sqliteicu.h
EXTHDR += \
$(TOP)/ext/rtree/sqlite3rtree.h
+EXTHDR += \
+ $(TOP)/ext/userauth/sqlite3userauth.h
# executables needed for testing
#
@@ -541,7 +577,8 @@ TESTPROGS = \
sqlite3$(TEXE) \
sqlite3_analyzer$(TEXE) \
sqldiff$(TEXE) \
- dbhash$(TEXE)
+ dbhash$(TEXE) \
+ sqltclsh$(TEXE)
# Databases containing fuzzer test cases
#
@@ -549,7 +586,11 @@ FUZZDATA = \
$(TOP)/test/fuzzdata1.db \
$(TOP)/test/fuzzdata2.db \
$(TOP)/test/fuzzdata3.db \
- $(TOP)/test/fuzzdata4.db
+ $(TOP)/test/fuzzdata4.db \
+ $(TOP)/test/fuzzdata5.db \
+ $(TOP)/test/fuzzdata6.db \
+ $(TOP)/test/fuzzdata7.db \
+ $(TOP)/test/fuzzdata8.db
# Standard options to testfixture
#
@@ -557,17 +598,79 @@ TESTOPTS = --verbose=file --output=test-out.txt
# Extra compiler options for various shell tools
#
-SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4
-# SHELL_OPT += -DSQLITE_ENABLE_FTS5
+SHELL_OPT += -DSQLITE_DQS=0
+SHELL_OPT += -DSQLITE_ENABLE_FTS4
+#SHELL_OPT += -DSQLITE_ENABLE_FTS5
+SHELL_OPT += -DSQLITE_ENABLE_RTREE
SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
-FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
-FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
+SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
+SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
+SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
+SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
+SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
+SHELL_OPT += -DSQLITE_STRICT_SUBTYPE=1
+FUZZERSHELL_OPT =
+FUZZCHECK_OPT += -I$(TOP)/test
+FUZZCHECK_OPT += -I$(TOP)/ext/recover
+FUZZCHECK_OPT += \
+ -DSQLITE_OSS_FUZZ \
+ -DSQLITE_ENABLE_BYTECODE_VTAB \
+ -DSQLITE_ENABLE_DBPAGE_VTAB \
+ -DSQLITE_ENABLE_DBSTAT_VTAB \
+ -DSQLITE_ENABLE_BYTECODE_VTAB \
+ -DSQLITE_ENABLE_DESERIALIZE \
+ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
+ -DSQLITE_ENABLE_FTS3_PARENTHESIS \
+ -DSQLITE_ENABLE_FTS4 \
+ -DSQLITE_ENABLE_FTS5 \
+ -DSQLITE_ENABLE_GEOPOLY \
+ -DSQLITE_ENABLE_MATH_FUNCTIONS \
+ -DSQLITE_ENABLE_MEMSYS5 \
+ -DSQLITE_ENABLE_NORMALIZE \
+ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \
+ -DSQLITE_ENABLE_PREUPDATE_HOOK \
+ -DSQLITE_ENABLE_RTREE \
+ -DSQLITE_ENABLE_SESSION \
+ -DSQLITE_ENABLE_STMTVTAB \
+ -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
+ -DSQLITE_ENABLE_STAT4 \
+ -DSQLITE_ENABLE_STMT_SCANSTATUS \
+ -DSQLITE_MAX_MEMORY=50000000 \
+ -DSQLITE_MAX_MMAP_SIZE=0 \
+ -DSQLITE_OMIT_LOAD_EXTENSION \
+ -DSQLITE_PRINTF_PRECISION_LIMIT=1000 \
+ -DSQLITE_PRIVATE="" \
+ -DSQLITE_STRICT_SUBTYPE=1 \
+ -DSQLITE_STATIC_RANDOMJSON
+
+FUZZCHECK_SRC += $(TOP)/test/fuzzcheck.c
+FUZZCHECK_SRC += $(TOP)/test/ossfuzz.c
+FUZZCHECK_SRC += $(TOP)/test/fuzzinvariants.c
+FUZZCHECK_SRC += $(TOP)/ext/recover/dbdata.c
+FUZZCHECK_SRC += $(TOP)/ext/recover/sqlite3recover.c
+FUZZCHECK_SRC += $(TOP)/test/vt02.c
+FUZZCHECK_SRC += $(TOP)/ext/misc/percentile.c
+FUZZCHECK_SRC += $(TOP)/ext/misc/randomjson.c
+DBFUZZ_OPT =
+ST_OPT = -DSQLITE_OS_KV_OPTIONAL
+
+
+# In wasi-sdk builds, disable the CLI shell build in the "all" target.
+SQLITE3_SHELL_TARGET_ = sqlite3$(TEXE)
+SQLITE3_SHELL_TARGET_1 =
+SQLITE3_SHELL_TARGET = $(SQLITE3_SHELL_TARGET_@HAVE_WASI_SDK@)
+
+# Use $(libtclsqlite3.la_$(HAVE_TCL)) to resolve to either
+# libtclsqlite3.la or an empty value.
+libtclsqlite3.la_0 =
+libtclsqlite3.la_1 = libtclsqlite3.la
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
-all: sqlite3.h libsqlite3.la sqlite3$(TEXE) $(HAVE_TCL:1=libtclsqlite3.la)
+all: sqlite3.h libsqlite3.la $(SQLITE3_SHELL_TARGET) \
+ $(libtclsqlite3.la_$(HAVE_TCL))
Makefile: $(TOP)/Makefile.in
./config.status
@@ -586,17 +689,31 @@ libtclsqlite3.la: tclsqlite.lo libsqlite3.la
-version-info "8:6:8" \
-avoid-version
-sqlite3$(TEXE): $(TOP)/src/shell.c sqlite3.c
+sqlite3$(TEXE): shell.c sqlite3.c
$(LTLINK) $(READLINE_FLAGS) $(SHELL_OPT) -o $@ \
- $(TOP)/src/shell.c sqlite3.c \
+ shell.c sqlite3.c \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
-sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.lo sqlite3.h
- $(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.lo $(TLIBS)
+sqldiff$(TEXE): $(TOP)/tool/sqldiff.c $(TOP)/ext/misc/sqlite3_stdio.h sqlite3.lo sqlite3.h
+ $(LTLINK) -I$(TOP)/ext/misc -o $@ $(TOP)/tool/sqldiff.c sqlite3.lo $(TLIBS)
dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.lo sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/dbhash.c sqlite3.lo $(TLIBS)
+RSYNC_SRC = \
+ $(TOP)/tool/sqlite3_rsync.c \
+ sqlite3.c
+
+RSYNC_OPT = \
+ -DSQLITE_ENABLE_DBPAGE_VTAB \
+ -USQLITE_THREADSAFE \
+ -DSQLITE_THREADSAFE=0 \
+ -DSQLITE_OMIT_LOAD_EXTENSION \
+ -DSQLITE_OMIT_DEPRECATED
+
+sqlite3_rsync$(TEXE): $(RSYNC_SRC)
+ $(TCC) -o $@ $(RSYNC_OPT) $(RSYNC_SRC) $(TLIBS)
+
scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.lo
$(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \
$(TOP)/ext/misc/scrub.c sqlite3.lo $(TLIBS)
@@ -605,14 +722,68 @@ srcck1$(BEXE): $(TOP)/tool/srcck1.c
$(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c
sourcetest: srcck1$(BEXE) sqlite3.c
- ./srcck1 sqlite3.c
+ ./srcck1$(BEXE) sqlite3.c
+
+src-verify$(BEXE): $(TOP)/tool/src-verify.c
+ $(BCC) -o src-verify$(BEXE) $(TOP)/tool/src-verify.c
+
+verify-source: ./src-verify$(BEXE)
+ ./src-verify$(BEXE) $(TOP)
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(FUZZERSHELL_OPT) \
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
-fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
- $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS)
+fuzzcheck$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
+ $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
+
+fuzzcheck-asan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
+ $(LTLINK) -o $@ -fsanitize=address $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
+
+fuzzcheck-ubsan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
+ $(LTLINK) -o $@ -fsanitize=undefined $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
+
+# Usage: FUZZDB=filename make run-fuzzcheck
+#
+# Where filename is a fuzzcheck database, this target builds and runs
+# fuzzcheck, fuzzcheck-asan, and fuzzcheck-ubsan on that database.
+#
+# FUZZDB can be a glob pattern of two or more databases. Example:
+#
+# FUZZDB=test/fuzzdata*.db make run-fuzzcheck
+#
+run-fuzzcheck: fuzzcheck$(TEXE) fuzzcheck-asan$(TEXE) fuzzcheck-ubsan$(TEXE)
+ @if test "$(FUZZDB)" = ""; then echo 'ERROR: No FUZZDB specified. Rerun with FUZZDB=filename'; exit 1; fi
+ ./fuzzcheck$(TEXE) --spinner $(FUZZDB)
+ ./fuzzcheck-asan$(TEXE) --spinner $(FUZZDB)
+ ./fuzzcheck-ubsan$(TEXE) --spinner $(FUZZDB)
+
+ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
+ $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \
+ $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS)
+
+sessionfuzz$(TEXE): $(TOP)/test/sessionfuzz.c sqlite3.c sqlite3.h
+ $(LTLINK) -o $@ $(TOP)/test/sessionfuzz.c $(TLIBS)
+
+dbfuzz$(TEXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h
+ $(LTLINK) -o $@ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c $(TLIBS)
+
+DBFUZZ2_OPTS = \
+ -DSQLITE_THREADSAFE=0 \
+ -DSQLITE_OMIT_LOAD_EXTENSION \
+ -DSQLITE_DEBUG \
+ -DSQLITE_ENABLE_DBSTAT_VTAB \
+ -DSQLITE_ENABLE_BYTECODE_VTAB \
+ -DSQLITE_ENABLE_RTREE \
+ -DSQLITE_ENABLE_FTS4 \
+ -DSQLITE_ENABLE_FTS5
+
+dbfuzz2$(TEXE): $(TOP)/test/dbfuzz2.c sqlite3.c sqlite3.h
+ $(CC) $(OPT_FEATURE_FLAGS) $(OPTS) -I. -g -O0 \
+ -DSTANDALONE -o dbfuzz2 \
+ $(DBFUZZ2_OPTS) $(TOP)/test/dbfuzz2.c sqlite3.c $(TLIBS)
+ mkdir -p dbfuzz2-dir
+ cp $(TOP)/test/dbfuzz2-seed* dbfuzz2-dir
mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.lo \
@@ -632,13 +803,26 @@ mptest: mptester$(TEXE)
$(MPTEST2) --journalmode DELETE
+has_tclsh84:
+ sh $(TOP)/tool/cktclsh.sh 8.4 $(TCLSH_CMD)
+ touch has_tclsh84
+
+has_tclsh85:
+ sh $(TOP)/tool/cktclsh.sh 8.5 $(TCLSH_CMD)
+ touch has_tclsh85
+
+has_tclconfig:
+ @ if test x"$(HAVE_TCL)" != "x1"; then echo 'ERROR: Requires access to "tclConfig.sh" which "configure" was not able to locate'; exit 1; fi
+ touch has_tclconfig
+
+
# This target creates a directory named "tsrc" and fills it with
# copies of all of the C source code and header files needed to
# build on the target system. Some of the C source code and header
# files are automatically generated. This target takes care of
# all that automatic generation.
#
-.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c
+.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl has_tclsh84 fts5.c
rm -rf tsrc
mkdir tsrc
cp -f $(SRC) tsrc
@@ -648,11 +832,20 @@ mptest: mptester$(TEXE)
cp fts5.c fts5.h tsrc
touch .target_source
-sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
- $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl
- cp tsrc/shell.c tsrc/sqlite3ext.h .
+sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify$(BEXE) has_tclsh84
+ $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
+ cp tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
+sqlite3r.h: sqlite3.h has_tclsh84
+ $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
+
+sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84
+ cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
+ cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
+ cp $(TOP)/ext/recover/dbdata.c tsrc/
+ $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
+
sqlite3ext.h: .target_source
cp tsrc/sqlite3ext.h .
@@ -662,7 +855,7 @@ tclsqlite3.c: sqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
-sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
+sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
# Rule to build the amalgamation
@@ -676,6 +869,11 @@ lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
$(BCC) -o $@ $(TOP)/tool/lemon.c
cp $(TOP)/tool/lempar.c .
+# Rules to build the program that generates the source-id
+#
+mksourceid$(BEXE): $(TOP)/tool/mksourceid.c
+ $(BCC) -o $@ $(TOP)/tool/mksourceid.c
+
# Rules to build individual *.o files from generated *.c files. This
# applies to:
#
@@ -729,6 +927,9 @@ ctime.lo: $(TOP)/src/ctime.c $(HDR)
date.lo: $(TOP)/src/date.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/date.c
+dbpage.lo: $(TOP)/src/dbpage.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/dbpage.c
+
dbstat.lo: $(TOP)/src/dbstat.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/dbstat.c
@@ -756,6 +957,9 @@ hash.lo: $(TOP)/src/hash.c $(HDR)
insert.lo: $(TOP)/src/insert.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/insert.c
+json.lo: $(TOP)/src/json.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/json.c
+
legacy.lo: $(TOP)/src/legacy.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/legacy.c
@@ -783,6 +987,9 @@ mem3.lo: $(TOP)/src/mem3.c $(HDR)
mem5.lo: $(TOP)/src/mem5.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mem5.c
+memdb.lo: $(TOP)/src/memdb.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/memdb.c
+
memjournal.lo: $(TOP)/src/memjournal.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/memjournal.c
@@ -813,6 +1020,9 @@ pcache1.lo: $(TOP)/src/pcache1.c $(HDR) $(TOP)/src/pcache.h
os.lo: $(TOP)/src/os.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os.c
+os_kv.lo: $(TOP)/src/os_kv.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_kv.c
+
os_unix.lo: $(TOP)/src/os_unix.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_unix.c
@@ -861,6 +1071,9 @@ trigger.lo: $(TOP)/src/trigger.c $(HDR)
update.lo: $(TOP)/src/update.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/update.c
+upsert.lo: $(TOP)/src/upsert.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/upsert.c
+
utf.lo: $(TOP)/src/utf.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/utf.c
@@ -891,6 +1104,9 @@ vdbesort.lo: $(TOP)/src/vdbesort.c $(HDR)
vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbetrace.c
+vdbevtab.lo: $(TOP)/src/vdbevtab.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbevtab.c
+
vtab.lo: $(TOP)/src/vtab.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c
@@ -909,45 +1125,88 @@ wherecode.lo: $(TOP)/src/wherecode.c $(HDR)
whereexpr.lo: $(TOP)/src/whereexpr.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/whereexpr.c
+window.lo: $(TOP)/src/window.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/window.c
+
tclsqlite.lo: $(TOP)/src/tclsqlite.c $(HDR)
$(LTCOMPILE) -DUSE_TCL_STUBS=1 -c $(TOP)/src/tclsqlite.c
tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR)
- $(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c
+ $(LTCOMPILE) -DTCLSH -o $@ -c $(TOP)/src/tclsqlite.c
tclsqlite-stubs.lo: $(TOP)/src/tclsqlite.c $(HDR)
$(LTCOMPILE) -DUSE_TCL_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c
-tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
+tclsqlite3$(TEXE): has_tclconfig tclsqlite-shell.lo libsqlite3.la
$(LTLINK) -o $@ tclsqlite-shell.lo \
libsqlite3.la $(LIBTCL)
# Rules to build opcodes.c and opcodes.h
#
-opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl
+opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c
-opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl
+opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl has_tclsh84
cat parse.h $(TOP)/src/vdbe.c | $(TCLSH_CMD) $(TOP)/tool/mkopcodeh.tcl >opcodes.h
# Rules to build parse.c and parse.h - the outputs of lemon.
#
parse.h: parse.c
-parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/tool/addopcodes.tcl
+parse.c: $(TOP)/src/parse.y lemon$(BEXE)
cp $(TOP)/src/parse.y .
- rm -f parse.h
- ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
- mv parse.h parse.h.temp
- $(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h
+ ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) -S parse.y
-sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION
+sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
+sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION has_tclsh84
+ echo '#ifndef SQLITE_RESOURCE_VERSION' >$@
+ echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@
+ cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@
+ echo '#endif' >>sqlite3rc.h
+
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
$(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c
./mkkeywordhash$(BEXE) >keywordhash.h
+# Source and header files that shell.c depends on
+SHELL_DEP = \
+ $(TOP)/src/shell.c.in \
+ $(TOP)/ext/expert/sqlite3expert.c \
+ $(TOP)/ext/expert/sqlite3expert.h \
+ $(TOP)/ext/intck/sqlite3intck.c \
+ $(TOP)/ext/intck/sqlite3intck.h \
+ $(TOP)/ext/misc/appendvfs.c \
+ $(TOP)/ext/misc/base64.c \
+ $(TOP)/ext/misc/base85.c \
+ $(TOP)/ext/misc/completion.c \
+ $(TOP)/ext/misc/decimal.c \
+ $(TOP)/ext/misc/fileio.c \
+ $(TOP)/ext/misc/ieee754.c \
+ $(TOP)/ext/misc/memtrace.c \
+ $(TOP)/ext/misc/pcachetrace.c \
+ $(TOP)/ext/misc/percentile.c \
+ $(TOP)/ext/misc/regexp.c \
+ $(TOP)/ext/misc/series.c \
+ $(TOP)/ext/misc/sha1.c \
+ $(TOP)/ext/misc/shathree.c \
+ $(TOP)/ext/misc/sqlar.c \
+ $(TOP)/ext/misc/sqlite3_stdio.c \
+ $(TOP)/ext/misc/sqlite3_stdio.h \
+ $(TOP)/ext/misc/uint.c \
+ $(TOP)/ext/misc/vfstrace.c \
+ $(TOP)/ext/misc/zipfile.c \
+ $(TOP)/ext/recover/dbdata.c \
+ $(TOP)/ext/recover/sqlite3recover.c \
+ $(TOP)/ext/recover/sqlite3recover.h \
+ $(TOP)/src/test_windirent.c \
+ $(TOP)/src/test_windirent.h
+
+shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl has_tclsh84
+ $(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c
+
+
# Rules to build the extension objects.
@@ -955,24 +1214,6 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
icu.lo: $(TOP)/ext/icu/icu.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/icu/icu.c
-fts2.lo: $(TOP)/ext/fts2/fts2.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2.c
-
-fts2_hash.lo: $(TOP)/ext/fts2/fts2_hash.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_hash.c
-
-fts2_icu.lo: $(TOP)/ext/fts2/fts2_icu.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_icu.c
-
-fts2_porter.lo: $(TOP)/ext/fts2/fts2_porter.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_porter.c
-
-fts2_tokenizer.lo: $(TOP)/ext/fts2/fts2_tokenizer.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c
-
-fts2_tokenizer1.lo: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c
-
fts3.lo: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c
@@ -1015,11 +1256,14 @@ fts3_write.lo: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
+userauth.lo: $(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR)
+ $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/userauth/userauth.c
+
sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c
-json1.lo: $(TOP)/ext/misc/json1.c
- $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c
+stmt.lo: $(TOP)/ext/misc/stmt.c
+ $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c
# FTS5 things
#
@@ -1040,14 +1284,14 @@ FTS5_SRC = \
$(TOP)/ext/fts5/fts5_varint.c \
$(TOP)/ext/fts5/fts5_vocab.c \
-fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon
+fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
cp $(TOP)/ext/fts5/fts5parse.y .
rm -f fts5parse.h
- ./lemon$(BEXE) $(OPTS) fts5parse.y
+ ./lemon$(BEXE) $(OPTS) -S fts5parse.y
fts5parse.h: fts5parse.c
-fts5.c: $(FTS5_SRC)
+fts5.c: $(FTS5_SRC) has_tclsh84
$(TCLSH_CMD) $(TOP)/ext/fts5/tool/mkfts5c.tcl
cp $(TOP)/ext/fts5/fts5.h .
@@ -1065,23 +1309,37 @@ sqlite3rbu.lo: $(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR)
# necessary because the test fixture requires non-API symbols which are
# hidden when the library is built via the amalgamation).
#
-TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
-TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
+TESTFIXTURE_FLAGS = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
+TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit
+TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
TESTFIXTURE_FLAGS += -DBUILD_sqlite
TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
+TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
+TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
+TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB
+TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC
+TESTFIXTURE_FLAGS += -DSQLITE_STATIC_RANDOMJSON
+TESTFIXTURE_FLAGS += -DSQLITE_STRICT_SUBTYPE=1
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
TESTFIXTURE_SRC1 = sqlite3.c
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
-testfixture$(TEXE): $(TESTFIXTURE_SRC)
+testfixture$(TEXE): has_tclconfig has_tclsh85 $(TESTFIXTURE_SRC)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \
-o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
+coretestprogs: testfixture$(BEXE) sqlite3$(BEXE)
+
+testprogs: $(TESTPROGS) srcck1$(BEXE) fuzzcheck$(TEXE) sessionfuzz$(TEXE)
+
# A very detailed test running most or all test cases
-fulltest: $(TESTPROGS) fuzztest
+fulltest: alltest fuzztest
+
+# Run most or all tcl test cases
+alltest: $(TESTPROGS)
./testfixture$(TEXE) $(TOP)/test/all.test $(TESTOPTS)
# Really really long testing
@@ -1093,25 +1351,69 @@ fulltestonly: $(TESTPROGS) fuzztest
./testfixture$(TEXE) $(TOP)/test/full.test
# Fuzz testing
-fuzztest: fuzzcheck$(TEXE) $(FUZZDATA)
+#
+# WARNING: When the "fuzztest" target is run by the testrunner.tcl script,
+# it does not actually run this code. Instead, it schedules equivalent
+# commands. Therefore, if this target is updated, then code in
+# testrunner_data.tcl (search for "trd_fuzztest_data") must also be updated.
+#
+fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE)
./fuzzcheck$(TEXE) $(FUZZDATA)
+ ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
+
+valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE)
+ valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
+ valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
+
+# The veryquick.test TCL tests.
+#
+tcltest: ./testfixture$(TEXE)
+ ./testfixture$(TEXE) $(TOP)/test/veryquick.test $(TESTOPTS)
+
+# Runs all the same tests cases as the "tcltest" target but uses
+# the testrunner.tcl script to run them in multiple cores
+# concurrently.
+testrunner: testfixture$(TEXE)
+ ./testfixture$(TEXE) $(TOP)/test/testrunner.tcl
+
+# This is the testing target preferred by the core SQLite developers.
+# It runs tests under a standard configuration, regardless of how
+# ./configure was run. The devs run "make devtest" prior to each
+# check-in, at a minimum. Probably other tests too, but at least this
+# one.
+#
+devtest: srctree-check sourcetest
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)
-fastfuzztest: fuzzcheck$(TEXE) $(FUZZDATA)
- ./fuzzcheck$(TEXE) --limit-mem 100M $(FUZZDATA)
+mdevtest: srctree-check has_tclsh85
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)
-valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA)
- valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
+sdevtest: has_tclsh85
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest $(TSTRNNR_OPTS)
+
+# Validate that various generated files in the source tree
+# are up-to-date.
+#
+srctree-check: $(TOP)/tool/srctree-check.tcl
+ $(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl
+
+# Testing for a release
+#
+releasetest: srctree-check has_tclsh85 verify-source
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl release $(TSTRNNR_OPTS)
# Minimal testing that runs in less than 3 minutes
#
quicktest: ./testfixture$(TEXE)
./testfixture$(TEXE) $(TOP)/test/extraquick.test $(TESTOPTS)
-# This is the common case. Run many tests that do not take too long,
-# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
+# Try to run tests on whatever options are specified by the
+# ./configure. The developers seldom use this target. Instead
+# they use "make devtest" which runs tests on a standard set of
+# options regardless of how SQLite is configured. This "test"
+# target is provided for legacy only.
#
-test: $(TESTPROGS) sourcetest fastfuzztest
- ./testfixture$(TEXE) $(TOP)/test/veryquick.test $(TESTOPTS)
+test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
# Run a test using valgrind. This can take a really long time
# because valgrind is so much slower than a native machine.
@@ -1126,18 +1428,47 @@ valgrindtest: $(TESTPROGS) valgrindfuzz
smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
./testfixture$(TEXE) $(TOP)/test/main.test $(TESTOPTS)
-sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
- echo "#define TCLSH 2" > $@
- echo "#define SQLITE_ENABLE_DBSTAT_VTAB 1" >> $@
- cat sqlite3.c $(TOP)/src/tclsqlite.c >> $@
- echo "static const char *tclsh_main_loop(void){" >> $@
- echo "static const char *zMainloop = " >> $@
- $(TCLSH_CMD) $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@
- echo "; return zMainloop; }" >> $@
+shelltest:
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl release shell
+
+sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in has_tclsh85
+ $(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
-sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
+sqlite3_analyzer$(TEXE): has_tclconfig sqlite3_analyzer.c
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
+sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in has_tclsh85
+ $(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
+
+sqltclsh$(TEXE): has_tclconfig sqltclsh.c
+ $(LTLINK) sqltclsh.c -o $@ $(LIBTCL) $(TLIBS)
+
+sqlite3_expert$(TEXE): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c
+ $(LTLINK) $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c -o sqlite3_expert $(TLIBS)
+
+CHECKER_DEPS =\
+ $(TOP)/tool/mkccode.tcl \
+ sqlite3.c \
+ $(TOP)/src/tclsqlite.c \
+ $(TOP)/ext/repair/sqlite3_checker.tcl \
+ $(TOP)/ext/repair/checkindex.c \
+ $(TOP)/ext/repair/checkfreelist.c \
+ $(TOP)/ext/misc/btreeinfo.c \
+ $(TOP)/ext/repair/sqlite3_checker.c.in
+
+sqlite3_checker.c: $(CHECKER_DEPS) has_tclsh85
+ $(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/ext/repair/sqlite3_checker.c.in >$@
+
+sqlite3_checker$(TEXE): has_tclconfig sqlite3_checker.c
+ $(LTLINK) sqlite3_checker.c -o $@ $(LIBTCL) $(TLIBS)
+
+dbdump$(TEXE): $(TOP)/ext/misc/dbdump.c sqlite3.lo
+ $(LTLINK) -DDBDUMP_STANDALONE -o $@ \
+ $(TOP)/ext/misc/dbdump.c sqlite3.lo $(TLIBS)
+
+dbtotxt$(TEXE): $(TOP)/tool/dbtotxt.c
+ $(LTLINK)-o $@ $(TOP)/tool/dbtotxt.c
+
showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS)
@@ -1150,22 +1481,42 @@ showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo
showwal$(TEXE): $(TOP)/tool/showwal.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showwal.c sqlite3.lo $(TLIBS)
+showshm$(TEXE): $(TOP)/tool/showshm.c
+ $(LTLINK) -o $@ $(TOP)/tool/showshm.c
+
+index_usage$(TEXE): $(TOP)/tool/index_usage.c sqlite3.lo
+ $(LTLINK) $(SHELL_OPT) -o $@ $(TOP)/tool/index_usage.c sqlite3.lo $(TLIBS)
+
changeset$(TEXE): $(TOP)/ext/session/changeset.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/ext/session/changeset.c sqlite3.lo $(TLIBS)
+changesetfuzz$(TEXE): $(TOP)/ext/session/changesetfuzz.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/ext/session/changesetfuzz.c sqlite3.lo $(TLIBS)
+
rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/rollback-test.c sqlite3.lo $(TLIBS)
+atrc$(TEXX): $(TOP)/test/atrc.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/test/atrc.c sqlite3.lo $(TLIBS)
+
LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h
$(LTLINK) -I. -o $@ $(TOP)/tool/logest.c
wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.lo $(TLIBS)
-speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.lo
- $(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS)
+speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.c Makefile
+ $(LTLINK) $(ST_OPT) -o $@ $(TOP)/test/speedtest1.c sqlite3.c $(TLIBS)
+
+startup$(TEXE): $(TOP)/test/startup.c sqlite3.c
+ $(CC) -Os -g -DSQLITE_THREADSAFE=0 -o $@ $(TOP)/test/startup.c sqlite3.c $(TLIBS)
+
+KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ
+
+kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c
+ $(LTLINK) $(KV_OPT) -o $@ $(TOP)/test/kvtest.c sqlite3.c $(TLIBS)
-rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.lo
+rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.lo
$(LTLINK) -I. -o $@ $(TOP)/ext/rbu/rbu.c sqlite3.lo $(TLIBS)
loadfts$(EXE): $(TOP)/tool/loadfts.c libsqlite3.la
@@ -1176,24 +1527,32 @@ loadfts$(EXE): $(TOP)/tool/loadfts.c libsqlite3.la
# releasetest.tcl script.
#
VALIDIDS=' sqlite3(changeset|changegroup|session)?_'
-checksymbols: sqlite3.lo
- nm -g --defined-only sqlite3.lo | egrep -v $(VALIDIDS); test $$? -ne 0
+checksymbols: sqlite3.o
+ nm -g --defined-only sqlite3.o
+ nm -g --defined-only sqlite3.o | egrep -v $(VALIDIDS); test $$? -ne 0
echo '0 errors out of 1 tests'
# Build the amalgamation-autoconf package. The amalamgation-tarball target builds
# a tarball named for the version number. Ex: sqlite-autoconf-3110000.tar.gz.
# The snapshot-tarball target builds a tarball named by the SHA1 hash
#
-amalgamation-tarball: sqlite3.c
+amalgamation-tarball: sqlite3.c sqlite3rc.h
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal
-snapshot-tarball: sqlite3.c
+snapshot-tarball: sqlite3.c sqlite3rc.h
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
+# Build a ZIP archive containing various command-line tools.
+#
+tool-zip: testfixture$(TEXE) sqlite3$(TEXE) sqldiff$(TEXE) \
+ sqlite3_analyzer$(TEXE) sqlite3_rsync$(TEXE) $(TOP)/tool/mktoolzip.tcl
+ strip sqlite3$(TEXE) sqldiff$(TEXE) sqlite3_analyzer$(TEXE) sqlite3_rsync$(TEXE)
+ ./testfixture$(TEXE) $(TOP)/tool/mktoolzip.tcl
+
# The next two rules are used to support the "threadtest" target. Building
# threadtest runs a few thread-safety tests that are implemented in C. This
# target is invoked by the releasetest.tcl script.
-#
+#
THREADTEST3_SRC = $(TOP)/test/threadtest3.c \
$(TOP)/test/tt3_checkpoint.c \
$(TOP)/test/tt3_index.c \
@@ -1207,16 +1566,21 @@ threadtest3$(TEXE): sqlite3.lo $(THREADTEST3_SRC)
threadtest: threadtest3$(TEXE)
./threadtest3$(TEXE)
-releasetest:
- $(TCLSH_CMD) $(TOP)/test/releasetest.tcl
+threadtest5: sqlite3.c $(TOP)/test/threadtest5.c
+ $(LTLINK) $(TOP)/test/threadtest5.c sqlite3.c -o $@ $(TLIBS)
# Standard install and cleanup targets
#
lib_install: libsqlite3.la
$(INSTALL) -d $(DESTDIR)$(libdir)
$(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir)
-
-install: sqlite3$(TEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install}
+
+# Use $(tcl_install_$(HAVE_TCL)) to resolve to either tclextension-install or
+# an empty value.
+tcl_install_0 =
+tcl_install_1 = tclextension-install
+
+install: sqlite3$(TEXE) lib_install sqlite3.h sqlite3.pc $(tcl_install_$(HAVE_TCL))
$(INSTALL) -d $(DESTDIR)$(bindir)
$(LTINSTALL) sqlite3$(TEXE) $(DESTDIR)$(bindir)
$(INSTALL) -d $(DESTDIR)$(includedir)
@@ -1225,44 +1589,64 @@ install: sqlite3$(TEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_instal
$(INSTALL) -d $(DESTDIR)$(pkgconfigdir)
$(INSTALL) -m 0644 sqlite3.pc $(DESTDIR)$(pkgconfigdir)
-pkgIndex.tcl:
- echo 'package ifneeded sqlite3 $(RELEASE) [list load $(TCLLIBDIR)/libtclsqlite3$(SHLIB_SUFFIX) sqlite3]' > $@
-tcl_install: lib_install libtclsqlite3.la pkgIndex.tcl
- $(INSTALL) -d $(DESTDIR)$(TCLLIBDIR)
- $(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)
- rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a
- $(INSTALL) -m 0644 pkgIndex.tcl $(DESTDIR)$(TCLLIBDIR)
-
-clean:
- rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la
- rm -f sqlite3.h opcodes.*
- rm -rf .libs .deps
- rm -f lemon$(BEXE) lempar.c parse.* sqlite*.tar.gz
- rm -f mkkeywordhash$(BEXE) keywordhash.h
- rm -f *.da *.bb *.bbg gmon.out
- rm -rf tsrc .target_source
- rm -f tclsqlite3$(TEXE)
- rm -f testfixture$(TEXE) test.db
+# Build the SQLite TCL extension in a way that make it compatible
+# with whatever version of TCL is running as $TCLSH_CMD, possibly defined
+# by --with-tclsh=
+#
+tclextension: tclsqlite3.c
+ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --build-only --cc "$(CC)" $(CFLAGS) $(OPT_FEATURE_FLAGS) $(OPTS)
+
+# Install the SQLite TCL extension in a way that is appropriate for $TCLSH_CMD
+# to find it.
+#
+tclextension-install: tclsqlite3.c
+ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --destdir "$(DESTDIR)" --cc "$(CC)" $(CFLAGS) $(OPT_FEATURE_FLAGS) $(OPTS)
+
+# Install the SQLite TCL extension that is used by $TCLSH_CMD
+#
+tclextension-uninstall:
+ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --uninstall
+
+# List all installed the SQLite TCL extension that is are accessible
+# by $TCLSH_CMD, included prior versions.
+#
+tclextension-list:
+ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info
+
+
+# Remove build products sufficient so that subsequent makes will recompile
+# everything from scratch. Do not remove:
+#
+# * test results and test logs
+# * output from ./configure
+#
+tidy:
+ rm -f *.lo *.la *.o *.c *.da *.bb *.bbg gmon.* *.rws sqlite3$(TEXE)
+ rm -f fts5.h keywordhash.h opcodes.h sqlite3.h sqlite3ext.h sqlite3session.h
+ rm -rf .libs .deps tsrc .target_source
+ rm -f lemon$(BEXE) sqlite*.tar.gz
+ rm -f mkkeywordhash$(BEXE) mksourceid$(BEXE)
+ rm -f parse.* fts5parse.*
+ rm -f tclsqlite3$(TEXE) $(TESTPROGS)
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
- rm -f wordcount$(TEXE) changeset$(TEXE)
- rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
- rm -f sqlite3.c
- rm -f sqlite3rc.h
- rm -f shell.c sqlite3ext.h
- rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c
- rm -f sqlite-*-output.vsix
- rm -f mptester mptester.exe
- rm -f rbu rbu.exe
- rm -f srcck1 srcck1.exe
- rm -f fuzzershell fuzzershell.exe
- rm -f fuzzcheck fuzzcheck.exe
- rm -f sqldiff sqldiff.exe
- rm -f dbhash dbhash.exe
- rm -f fts5.* fts5parse.*
+ rm -f wordcount$(TEXE) changeset$(TEXE) version-info$(TEXE)
+ rm -f *.dll *.lib *.exp *.def *.pc *.vsix *.so *.dylib pkgIndex.tcl
+ rm -f sqlite3_analyzer$(TEXE) sqlite3_rsync$(TEXE)
+ rm -f mptester$(TEXE) rbu$(TEXE) srcck1$(TEXE)
+ rm -f fuzzershell$(TEXE) fuzzcheck$(TEXE) sqldiff$(TEXE) dbhash$(TEXE)
+ rm -f threadtest5$(TEXE)
+ rm -f src-verify$(BEXE) has_tclsh* has_tclconfig
+
+# Removes build products and test logs. Retains ./configure outputs.
+#
+clean: tidy
+ rm -rf omittest* testrunner* testdir*
+# Clean up everything. No exceptions.
+#
distclean: clean
- rm -f config.h config.log config.status libtool Makefile sqlite3.pc
+ rm -f sqlite_cfg.h config.log config.status Makefile $(LIBTOOL)
#
# Windows section
@@ -1281,3 +1665,34 @@ sqlite3.def: $(REAL_LIBOBJ)
sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def
$(TCC) -shared -o $@ sqlite3.def \
-Wl,"--strip-all" $(REAL_LIBOBJ)
+
+#
+# Fiddle app
+#
+fiddle: sqlite3.c shell.c
+ make -C ext/wasm fiddle emcc_opt=-Os
+
+#
+# Spell-checking for source comments
+# The sources checked are either C sources or C source templates.
+# Their comments are extracted and processed through aspell using
+# a custom dictionary that contains scads of odd identifiers that
+# find their way into the comments.
+#
+# Currently, this target is setup to be "made" in-tree only.
+# The output is ephemeral. Redirect it to guide spelling fixups,
+# either to correct spelling or add words to tool/custom.txt.
+#
+./custom.rws: ./tool/custom.txt
+ @echo 'Updating custom dictionary from tool/custom.txt'
+ aspell --lang=en create master ./custom.rws < $<
+
+misspell: ./custom.rws has_tclsh84
+ $(TCLSH_CMD) ./tool/spellsift.tcl ./src/*.c ./src/*.h ./src/*.in
+
+#
+# tool/version-info: a utility for emitting sqlite3 version info
+# in various forms.
+#
+version-info$(TEXE): $(TOP)/tool/version-info.c Makefile sqlite3.h
+ $(LTLINK) $(ST_OPT) -o $@ $(TOP)/tool/version-info.c
diff --git a/sqlite/Makefile.linux-gcc b/sqlite/Makefile.linux-gcc
index 1491a4b0..fe7349ad 100644
--- a/sqlite/Makefile.linux-gcc
+++ b/sqlite/Makefile.linux-gcc
@@ -19,15 +19,9 @@ TOP = ../sqlite
#### C Compiler and options for use in building executables that
# will run on the platform that is doing the build.
#
-BCC = gcc -g -O2
+BCC = gcc -g -O0
#BCC = /opt/ancic/bin/c89 -0
-#### If the target operating system supports the "usleep()" system
-# call, then define the HAVE_USLEEP macro for all C modules.
-#
-#USLEEP =
-USLEEP = -DHAVE_USLEEP=1
-
#### If you want the SQLite library to be safe for use within a
# multi-threaded program, then define the following macro
# appropriately:
@@ -38,8 +32,8 @@ THREADSAFE = -DTHREADSAFE=0
#### Specify any extra linker options needed to make the library
# thread safe
#
-#THREADLIB = -lpthread
-THREADLIB =
+THREADLIB = -lpthread -lm -ldl
+#THREADLIB =
#### Specify any extra libraries needed to access required functions.
#
@@ -54,11 +48,9 @@ TLIBS =
# You can make the library go almost twice as fast if you compile
# with -DNDEBUG=1
#
-#OPTS = -DSQLITE_DEBUG=2
-#OPTS = -DSQLITE_DEBUG=1
-#OPTS =
-OPTS = -DNDEBUG=1
-OPTS += -DHAVE_FDATASYNC=1
+OPTS += -DSQLITE_DEBUG=1
+OPTS += -DSQLITE_ENABLE_WHERETRACE
+OPTS += -DSQLITE_ENABLE_SELECTTRACE
#### The suffix to add to executable files. ".exe" for windows.
# Nothing for unix.
@@ -70,7 +62,7 @@ EXE =
# will run on the target platform. This is usually the same
# as BCC, unless you are cross-compiling.
#
-TCC = gcc -O6
+TCC = gcc -O0
#TCC = gcc -g -O0 -Wall
#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6
@@ -91,18 +83,12 @@ SHPREFIX = lib
#### Extra compiler options needed for programs that use the TCL library.
#
-#TCL_FLAGS =
-#TCL_FLAGS = -DSTATIC_BUILD=1
-TCL_FLAGS = -I/home/drh/tcltk/8.5linux
-#TCL_FLAGS = -I/home/drh/tcltk/8.5win -DSTATIC_BUILD=1
-#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux
+TCL_FLAGS = -I/home/drh/tcl/include/tcl8.6
#### Linker options needed to link against the TCL library.
#
#LIBTCL = -ltcl -lm -ldl
-LIBTCL = /home/drh/tcltk/8.5linux/libtcl8.5g.a -lm -ldl
-#LIBTCL = /home/drh/tcltk/8.5win/libtcl85s.a -lmsvcrt
-#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc
+LIBTCL = /home/drh/tcl/lib/libtcl8.6.a -lm -lpthread -ldl -lz
#### Additional objects for SQLite library when TCL support is enabled.
#TCLOBJ =
diff --git a/sqlite/Makefile.msc b/sqlite/Makefile.msc
index cd866ce4..1e208491 100644
--- a/sqlite/Makefile.msc
+++ b/sqlite/Makefile.msc
@@ -18,10 +18,38 @@ USE_AMALGAMATION = 1
!ENDIF
# <>
+# Optionally set EXTRA_SRC to a list of C files to append to
+# the generated sqlite3.c.
+#
+!IFNDEF EXTRA_SRC
+EXTRA_SRC =
+!ENDIF
+
# Set this non-0 to enable full warnings (-W4, etc) when compiling.
#
!IFNDEF USE_FULLWARN
-USE_FULLWARN = 0
+USE_FULLWARN = 1
+!ENDIF
+
+# Set this non-0 to enable treating warnings as errors (-WX, etc) when
+# compiling.
+#
+!IFNDEF USE_FATAL_WARN
+USE_FATAL_WARN = 0
+!ENDIF
+
+# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
+# has no effect if (any) optimizations are enabled.
+#
+!IFNDEF USE_RUNTIME_CHECKS
+USE_RUNTIME_CHECKS = 0
+!ENDIF
+
+# Set this non-0 to create a SQLite amalgamation file that excludes the
+# various built-in extensions.
+#
+!IFNDEF MINIMAL_AMALGAMATION
+MINIMAL_AMALGAMATION = 0
!ENDIF
# Set this non-0 to use "stdcall" calling convention for the core library
@@ -31,6 +59,21 @@ USE_FULLWARN = 0
USE_STDCALL = 0
!ENDIF
+# Use the USE_SEH=0 option on the nmake command line to omit structured
+# exception handling (SEH) support. SEH is on by default.
+#
+!IFNDEF USE_SEH
+USE_SEH = 1
+!ENDIF
+
+# Use STATICALLY_LINK_TCL=1 to statically link against TCL
+#
+!IFNDEF STATICALLY_LINK_TCL
+STATICALLY_LINK_TCL = 0
+!ELSEIF $(STATICALLY_LINK_TCL)!=0
+CCOPTS = $(CCOPTS) -DSTATIC_BUILD
+!ENDIF
+
# Set this non-0 to have the shell executable link against the core dynamic
# link library.
#
@@ -52,7 +95,7 @@ API_ARMOR = 0
!IFNDEF NO_WARN
!IF $(USE_FULLWARN)!=0
NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4130 -wd4152 -wd4189 -wd4206
-NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4305 -wd4306 -wd4702 -wd4706
+NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
@@ -71,6 +114,29 @@ SPLIT_AMALGAMATION = 0
!ENDIF
# <>
+# Set this non-0 to have this makefile assume the Tcl shell executable
+# (tclsh*.exe) is available in the PATH. By default, this is disabled
+# for compatibility with older build environments. This setting only
+# applies if TCLSH_CMD is not set manually.
+#
+!IFNDEF USE_TCLSH_IN_PATH
+USE_TCLSH_IN_PATH = 0
+!ENDIF
+
+# Set this non-0 to use zlib, possibly compiling it from source code.
+#
+!IFNDEF USE_ZLIB
+USE_ZLIB = 0
+!ENDIF
+
+# Set this non-0 to build zlib from source code. This is enabled by
+# default and in that case it will be assumed that the ZLIBDIR macro
+# points to the top-level source code directory for zlib.
+#
+!IFNDEF BUILD_ZLIB
+BUILD_ZLIB = 1
+!ENDIF
+
# Set this non-0 to use the International Components for Unicode (ICU).
#
!IFNDEF USE_ICU
@@ -174,6 +240,12 @@ WIN32HEAP = 0
OSTRACE = 0
!ENDIF
+# enable address sanitizer using ASAN=1 on the command-line.
+#
+!IFNDEF ASAN
+ASAN = 0
+!ENDIF
+
# Set this to one of the following values to enable various debugging
# features. Each level includes the debugging options from the previous
# levels. Currently, the recognized values for DEBUG are:
@@ -190,6 +262,16 @@ OSTRACE = 0
DEBUG = 0
!ENDIF
+# <>
+# By default, use --linemacros=1 argument to the mksqlite3c.tcl tool, which
+# is used to build the amalgamation. This can be turned off to ease debug
+# of the amalgamation away from the source tree.
+#
+!IFNDEF NO_LINEMACROS
+NO_LINEMACROS = 0
+!ENDIF
+# <>
+
# Enable use of available compiler optimizations? Normally, this should be
# non-zero. Setting this to zero, thus disabling all compiler optimizations,
# can be useful for testing.
@@ -204,6 +286,12 @@ OPTIMIZATIONS = 2
SESSION = 0
!ENDIF
+# Set this to non-0 to enable support for the rbu extension.
+#
+!IFNDEF RBU
+RBU = 0
+!ENDIF
+
# Set the source code file to be used by executables and libraries when
# they need the amalgamation.
#
@@ -263,15 +351,64 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
!ENDIF
!ENDIF
+# <>
+# These are the names of the customized Tcl header files used by various parts
+# of this makefile when the stdcall calling convention is in use. It is not
+# used for any other purpose.
+#
+!IFNDEF SQLITETCLH
+SQLITETCLH = sqlite_tcl.h
+!ENDIF
+
+!IFNDEF SQLITETCLDECLSH
+SQLITETCLDECLSH = sqlite_tclDecls.h
+!ENDIF
+
+# This is the name to use for the dynamic link library (DLL) containing the
+# Tcl bindings for SQLite.
+#
+!IFNDEF SQLITE3TCLDLL
+SQLITE3TCLDLL = tclsqlite3.dll
+!ENDIF
+
+# These are the additional targets that the targets that integrate with the
+# Tcl library should depend on when compiling, etc.
+#
+!IFNDEF SQLITE_TCL_DEP
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+SQLITE_TCL_DEP = $(SQLITETCLDECLSH) $(SQLITETCLH)
+!ELSE
+SQLITE_TCL_DEP =
+!ENDIF
+!ENDIF
+# <>
+
# These are the "standard" SQLite compilation options used when compiling for
# the Windows platform.
#
!IFNDEF OPT_FEATURE_FLAGS
+OPT_FEATURE_FLAGS = $(OPT_XTRA)
+!IF $(MINIMAL_AMALGAMATION)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
+!ENDIF
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
!ENDIF
+# Additional feature-options above and beyond what are normally used can be
+# be added using OPTIONS=.... on the command-line. These values are
+# appended to the OPT_FEATURE_FLAGS variable.
+#
+!IFDEF OPTIONS
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) $(OPTIONS)
+!ENDIF
+
# Should the session extension be enabled? If so, add compilation options
# to enable it.
#
@@ -280,6 +417,24 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
!ENDIF
+# Always enable math functions on Windows
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
+
+# Should the rbu extension be enabled? If so, add compilation options
+# to enable it.
+#
+!IF $(RBU)!=0
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
+!ENDIF
+
+# Should structured exception handling (SEH) be enabled for WAL mode in
+# the core library? It is on by default. Only omit it if the
+# USE_SEH=0 option is provided on the nmake command-line.
+#
+!IF $(USE_SEH)==0
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
+!ENDIF
+
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#
@@ -417,9 +572,9 @@ UCRTLIBPATH = $(UCRTLIBPATH:\\=\)
# will run on the platform that is doing the build.
#
!IF $(USE_FULLWARN)!=0
-BCC = $(NCC) -nologo -W4 $(CCOPTS) $(BCCOPTS)
+BCC = $(NCC) -nologo -W4 -Fd$*.pdb $(CCOPTS) $(BCCOPTS)
!ELSE
-BCC = $(NCC) -nologo -W3 $(CCOPTS) $(BCCOPTS)
+BCC = $(NCC) -nologo -W3 -Fd$*.pdb $(CCOPTS) $(BCCOPTS)
!ENDIF
# Check if assembly code listings should be generated for the source
@@ -452,6 +607,12 @@ TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS)
TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS)
!ENDIF
+# Check if warnings should be treated as errors when compiling.
+#
+!IF $(USE_FATAL_WARN)!=0
+TCC = $(TCC) -WX
+!ENDIF
+
TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise
RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
@@ -464,20 +625,32 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
#
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
!IF "$(PLATFORM)"=="x86"
-CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
-SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
+CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+# <>
+TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
+# <>
!ELSE
!IFNDEF PLATFORM
-CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
-SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
+CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+# <>
+TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
+# <>
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
+# <>
+TEST_CCONV_OPTS =
+# <>
!ENDIF
!ENDIF
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
+# <>
+TEST_CCONV_OPTS =
+# <>
!ENDIF
# These are additional compiler options used for the core library.
@@ -531,6 +704,10 @@ SHELL_COMPILE_OPTS = $(SHELL_CCONV_OPTS)
!IFNDEF SHELL_CORE_SRC
!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
SHELL_CORE_SRC =
+# <>
+!ELSEIF $(USE_AMALGAMATION)==0
+SHELL_CORE_SRC =
+# <>
!ELSE
SHELL_CORE_SRC = $(SQLITE3C)
!ENDIF
@@ -541,16 +718,33 @@ SHELL_CORE_SRC = $(SQLITE3C)
!IFNDEF SHELL_CORE_DEP
!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
SHELL_CORE_DEP = $(SQLITE3DLL)
+# <>
+!ELSEIF $(USE_AMALGAMATION)==0
+SHELL_CORE_DEP = libsqlite3.lib
+# <>
!ELSE
SHELL_CORE_DEP =
!ENDIF
!ENDIF
+# <>
+# If zlib support is enabled, add the dependencies for it.
+#
+!IF $(USE_ZLIB)!=0 && $(BUILD_ZLIB)!=0
+SHELL_CORE_DEP = zlib $(SHELL_CORE_DEP)
+TESTFIXTURE_DEP = zlib $(TESTFIXTURE_DEP)
+!ENDIF
+# <>
+
# This is the core library that the shell executable should link with.
#
!IFNDEF SHELL_CORE_LIB
!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
SHELL_CORE_LIB = $(SQLITE3LIB)
+# <>
+!ELSEIF $(USE_AMALGAMATION)==0
+SHELL_CORE_LIB = libsqlite3.lib
+# <>
!ELSE
SHELL_CORE_LIB =
!ENDIF
@@ -630,11 +824,34 @@ RCC = $(RCC) -I$(TOP)\ext\session
# options are necessary in order to allow debugging symbols to
# work correctly with Visual Studio when using the amalgamation.
#
+!IFNDEF MKSQLITE3C_TOOL
+!IF $(MINIMAL_AMALGAMATION)!=0
+MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c-noext.tcl
+!ELSE
+MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c.tcl
+!ENDIF
+!ENDIF
+
!IFNDEF MKSQLITE3C_ARGS
-!IF $(DEBUG)>1
-MKSQLITE3C_ARGS = --linemacros
+!IF $(DEBUG)>1 && $(NO_LINEMACROS)==0
+MKSQLITE3C_ARGS = --linemacros=1
+!ELSE
+MKSQLITE3C_ARGS = --linemacros=0
+!ENDIF
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+MKSQLITE3C_ARGS = $(MKSQLITE3C_ARGS) --useapicall
+!ENDIF
+!ENDIF
+
+# The mksqlite3h.tcl script accepts some options on the command line.
+# When compiling with stdcall support, some of these options are
+# necessary.
+#
+!IFNDEF MKSQLITE3H_ARGS
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+MKSQLITE3H_ARGS = --useapicall
!ELSE
-MKSQLITE3C_ARGS =
+MKSQLITE3H_ARGS =
!ENDIF
!ENDIF
# <>
@@ -657,6 +874,10 @@ RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
!IF $(DEBUG)>2
TCC = $(TCC) -DSQLITE_DEBUG=1
RCC = $(RCC) -DSQLITE_DEBUG=1
+!IF $(DYNAMIC_SHELL)==0
+TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
+RCC = $(RCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
+!ENDIF
!ENDIF
!IF $(DEBUG)>4 || $(OSTRACE)!=0
@@ -702,6 +923,13 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
!ENDIF
!ENDIF
+
+# Address sanitizer if ASAN=1
+#
+!IF $(ASAN)>0
+TCC = $(TCC) /fsanitize=address
+!ENDIF
+
# <>
# The locations of the Tcl header and library files. Also, the library that
# non-stubs enabled programs using Tcl must link against. These variables
@@ -709,24 +937,85 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
# prior to running nmake in order to match the actual installed location and
# version on this machine.
#
+!IF $(STATICALLY_LINK_TCL)!=0
+TCLSUFFIX = s
+!ENDIF
+!IFNDEF TCLSUFFIX
+TCLSUFFIX =
+!ENDIF
+
+!IFNDEF TCLDIR
+TCLDIR = C:\Tcl
+!ENDIF
+
+!IFNDEF TCLVERSION
+!IF EXISTS("$(TCLDIR)\lib\tcl90$(TCLSUFFIX).lib")
+TCLVERSION = 90
+!ELSEIF EXISTS("$(TCLDIR)\lib\tcl86$(TCLSUFFIX).lib")
+TCLVERSION = 86
+!ELSEIF EXISTS("$(TCLDIR)\lib\tcl86t.lib")
+TCLSUFFIX = t
+TCLVERSION = 86
+!ELSE
+TCLVERSION = 90
+!ENDIF
+!ENDIF
+
!IFNDEF TCLINCDIR
-TCLINCDIR = c:\tcl\include
+TCLINCDIR = $(TCLDIR)\include
!ENDIF
!IFNDEF TCLLIBDIR
-TCLLIBDIR = c:\tcl\lib
+TCLLIBDIR = $(TCLDIR)\lib
!ENDIF
!IFNDEF LIBTCL
-LIBTCL = tcl85.lib
+LIBTCL = tcl$(TCLVERSION)$(TCLSUFFIX).lib
+!ENDIF
+
+!IFNDEF TCLLIBS
+!IF $(STATICALLY_LINK_TCL)!=0
+TCLLIBS = /NODEFAULTLIB:libucrt.lib netapi32.lib user32.lib ucrt.lib
+!ELSE
+TCLLIBS =
+!ENDIF
!ENDIF
!IFNDEF LIBTCLSTUB
-LIBTCLSTUB = tclstub85.lib
+!IF EXISTS("$(TCLLIBDIR)\tclstub$(TCLSUFFIX).lib")
+LIBTCLSTUB = tclstub$(TCLSUFFIX).lib
+!ELSE
+LIBTCLSTUB = tclstub$(TCLVERSION)$(TCLSUFFIX).lib
+!ENDIF
!ENDIF
!IFNDEF LIBTCLPATH
-LIBTCLPATH = c:\tcl\bin
+LIBTCLPATH = $(TCLDIR)\bin
+!ENDIF
+
+# The locations of the zlib header and library files. These variables
+# (ZLIBINCDIR, ZLIBLIBDIR, and ZLIBLIB) may be overridden via the environment
+# prior to running nmake in order to match the actual installed (or source
+# code) location on this machine.
+#
+!IFNDEF ZLIBDIR
+ZLIBDIR = $(TOP)\compat\zlib
+!ENDIF
+
+!IFNDEF ZLIBINCDIR
+ZLIBINCDIR = $(ZLIBDIR)
+!ENDIF
+
+!IFNDEF ZLIBLIBDIR
+ZLIBLIBDIR = $(ZLIBDIR)
+!ENDIF
+
+!IFNDEF ZLIBLIB
+!IF $(DYNAMIC_SHELL)!=0
+ZLIBLIB = zdll.lib
+!ELSE
+ZLIBLIB = zlib.lib
+!ENDIF
!ENDIF
# The locations of the ICU header and library files. These variables
@@ -734,12 +1023,16 @@ LIBTCLPATH = c:\tcl\bin
# prior to running nmake in order to match the actual installed location on
# this machine.
#
+!IFNDEF ICUDIR
+ICUDIR = $(TOP)\compat\icu
+!ENDIF
+
!IFNDEF ICUINCDIR
-ICUINCDIR = c:\icu\include
+ICUINCDIR = $(ICUDIR)\include
!ENDIF
!IFNDEF ICULIBDIR
-ICULIBDIR = c:\icu\lib
+ICULIBDIR = $(ICUDIR)\lib
!ENDIF
!IFNDEF LIBICU
@@ -752,7 +1045,19 @@ LIBICU = icuuc.lib icuin.lib
# specific Tcl shell to use.
#
!IFNDEF TCLSH_CMD
-TCLSH_CMD = tclsh85
+!IF EXISTS("$(TCLDIR)\bin\tclsh$(TCLVERSION).exe")
+TCLSH_CMD = $(TCLDIR)\bin\tclsh$(TCLVERSION).exe
+!ELSEIF EXISTS("$(TCLDIR)\bin\tclsh90.exe")
+TCLSH_CMD = $(TCLDIR)\bin\tclsh90.exe
+!ELSEIF EXISTS("$(TCLDIR)\bin\tclsh86.exe")
+TCLSH_CMD = $(TCLDIR)\bin\tclsh86.exe
+!ELSEIF EXISTS("$(TCLDIR)\bin\tclsh86t.exe")
+TCLSH_CMD = $(TCLDIR)\bin\tclsh86t.exe
+!ELSEIF EXISTS("$(TCLDIR)\bin\tclsh.exe")
+TCLSH_CMD = $(TCLDIR)\bin\tclsh.exe
+!ELSE
+TCLSH_CMD = tclsh
+!ENDIF
!ENDIF
# <>
@@ -835,6 +1140,10 @@ RCC = $(RCC) -D_DEBUG
!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0
TCC = $(TCC) -Od
BCC = $(BCC) -Od
+!IF $(USE_RUNTIME_CHECKS)!=0
+TCC = $(TCC) -RTC1
+BCC = $(BCC) -RTC1
+!ENDIF
!ELSEIF $(OPTIMIZATIONS)>=3
TCC = $(TCC) -Ox
BCC = $(BCC) -Ox
@@ -854,6 +1163,15 @@ BCC = $(BCC) -Zi
!ENDIF
# <>
+# If zlib support is enabled, add the compiler options for it.
+#
+!IF $(USE_ZLIB)!=0
+TCC = $(TCC) -DSQLITE_HAVE_ZLIB=1
+RCC = $(RCC) -DSQLITE_HAVE_ZLIB=1
+TCC = $(TCC) -I$(ZLIBINCDIR)
+RCC = $(RCC) -I$(ZLIBINCDIR)
+!ENDIF
+
# If ICU support is enabled, add the compiler options for it.
#
!IF $(USE_ICU)!=0
@@ -869,7 +1187,7 @@ RCC = $(RCC) -I$(ICUINCDIR)
# Command line prefixes for compiling code, compiling resources,
# linking, etc.
#
-LTCOMPILE = $(TCC) -Fo$@
+LTCOMPILE = $(TCC) -Fo$@ -Fd$*.pdb
LTRCOMPILE = $(RCC) -r
LTLIB = lib.exe
LTLINK = $(TCC) -Fe$@
@@ -877,7 +1195,7 @@ LTLINK = $(TCC) -Fe$@
# If requested, link to the RPCRT4 library.
#
!IF $(USE_RPCRT4_LIB)!=0
-LTLINK = $(LTLINK) rpcrt4.lib
+LTLIBS = $(LTLIBS) rpcrt4.lib
!ENDIF
# If a platform was set, force the linker to target that.
@@ -887,6 +1205,11 @@ LTLINK = $(LTLINK) rpcrt4.lib
!IFDEF PLATFORM
LTLINKOPTS = /NOLOGO /MACHINE:$(PLATFORM)
LTLIBOPTS = /NOLOGO /MACHINE:$(PLATFORM)
+!ELSEIF "$(VISUALSTUDIOVERSION)"=="12.0" || \
+ "$(VISUALSTUDIOVERSION)"=="14.0" || \
+ "$(VISUALSTUDIOVERSION)"=="15.0"
+LTLINKOPTS = /NOLOGO /MACHINE:x86
+LTLIBOPTS = /NOLOGO /MACHINE:x86
!ELSE
LTLINKOPTS = /NOLOGO
LTLIBOPTS = /NOLOGO
@@ -974,8 +1297,15 @@ LDFLAGS = $(LDOPTS)
# Start with the Tcl related linker options.
#
!IF $(NO_TCL)==0
-LTLIBPATHS = /LIBPATH:$(TCLLIBDIR)
-LTLIBS = $(LIBTCL)
+TCLLIBPATHS = $(TCLLIBPATHS) /LIBPATH:$(TCLLIBDIR)
+TCLLIBS = $(TCLLIBS) $(LIBTCL)
+!ENDIF
+
+# If zlib support is enabled, add the linker options for it.
+#
+!IF $(USE_ZLIB)!=0
+LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ZLIBLIBDIR)
+LTLIBS = $(LTLIBS) $(ZLIBLIB)
!ENDIF
# If ICU support is enabled, add the linker options for it.
@@ -994,26 +1324,28 @@ LTLIBS = $(LTLIBS) $(LIBICU)
#
LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
- callback.lo complete.lo ctime.lo date.lo dbstat.lo delete.lo \
+ callback.lo complete.lo ctime.lo \
+ date.lo dbpage.lo dbstat.lo delete.lo \
expr.lo fault.lo fkey.lo \
fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \
fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \
fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \
fts5.lo \
func.lo global.lo hash.lo \
- icu.lo insert.lo legacy.lo loadext.lo \
+ icu.lo insert.lo json.lo legacy.lo loadext.lo \
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
- memjournal.lo \
+ memdb.lo memjournal.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
- notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
+ notify.lo opcodes.lo os.lo os_kv.lo os_unix.lo os_win.lo \
pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
random.lo resolve.lo rowset.lo rtree.lo \
- sqlite3session.lo select.lo sqlite3rbu.lo status.lo \
+ sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmt.lo \
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
- update.lo util.lo vacuum.lo \
+ update.lo upsert.lo util.lo vacuum.lo \
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
- vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
- utf.lo vtab.lo
+ vdbetrace.lo vdbevtab.lo wal.lo walker.lo where.lo wherecode.lo \
+ whereexpr.lo \
+ window.lo utf.lo vtab.lo
# <>
# Object files for the amalgamation.
@@ -1057,6 +1389,7 @@ SRC00 = \
$(TOP)\src\complete.c \
$(TOP)\src\ctime.c \
$(TOP)\src\date.c \
+ $(TOP)\src\dbpage.c \
$(TOP)\src\dbstat.c \
$(TOP)\src\delete.c \
$(TOP)\src\expr.c \
@@ -1066,6 +1399,7 @@ SRC00 = \
$(TOP)\src\global.c \
$(TOP)\src\hash.c \
$(TOP)\src\insert.c \
+ $(TOP)\src\json.c \
$(TOP)\src\legacy.c \
$(TOP)\src\loadext.c \
$(TOP)\src\main.c \
@@ -1075,6 +1409,7 @@ SRC00 = \
$(TOP)\src\mem2.c \
$(TOP)\src\mem3.c \
$(TOP)\src\mem5.c \
+ $(TOP)\src\memdb.c \
$(TOP)\src\memjournal.c \
$(TOP)\src\mutex.c \
$(TOP)\src\mutex_noop.c \
@@ -1082,6 +1417,7 @@ SRC00 = \
$(TOP)\src\mutex_w32.c \
$(TOP)\src\notify.c \
$(TOP)\src\os.c \
+ $(TOP)\src\os_kv.c \
$(TOP)\src\os_unix.c \
$(TOP)\src\os_win.c
@@ -1107,6 +1443,7 @@ SRC01 = \
$(TOP)\src\trigger.c \
$(TOP)\src\utf.c \
$(TOP)\src\update.c \
+ $(TOP)\src\upsert.c \
$(TOP)\src\util.c \
$(TOP)\src\vacuum.c \
$(TOP)\src\vdbe.c \
@@ -1116,17 +1453,14 @@ SRC01 = \
$(TOP)\src\vdbemem.c \
$(TOP)\src\vdbesort.c \
$(TOP)\src\vdbetrace.c \
+ $(TOP)\src\vdbevtab.c \
$(TOP)\src\vtab.c \
$(TOP)\src\wal.c \
$(TOP)\src\walker.c \
$(TOP)\src\where.c \
$(TOP)\src\wherecode.c \
- $(TOP)\src\whereexpr.c
-
-# Shell source code files.
-#
-SRC02 = \
- $(TOP)\src\shell.c
+ $(TOP)\src\whereexpr.c \
+ $(TOP)\src\window.c
# Core miscellaneous files.
#
@@ -1163,20 +1497,6 @@ SRC05 = \
$(TOP)\src\wal.h \
$(TOP)\src\whereInt.h
-# Extension source code files, part 1.
-#
-SRC06 = \
- $(TOP)\ext\fts1\fts1.c \
- $(TOP)\ext\fts1\fts1_hash.c \
- $(TOP)\ext\fts1\fts1_porter.c \
- $(TOP)\ext\fts1\fts1_tokenizer1.c \
- $(TOP)\ext\fts2\fts2.c \
- $(TOP)\ext\fts2\fts2_hash.c \
- $(TOP)\ext\fts2\fts2_icu.c \
- $(TOP)\ext\fts2\fts2_porter.c \
- $(TOP)\ext\fts2\fts2_tokenizer.c \
- $(TOP)\ext\fts2\fts2_tokenizer1.c
-
# Extension source code files, part 2.
#
SRC07 = \
@@ -1197,17 +1517,7 @@ SRC07 = \
$(TOP)\ext\rtree\rtree.c \
$(TOP)\ext\session\sqlite3session.c \
$(TOP)\ext\rbu\sqlite3rbu.c \
- $(TOP)\ext\misc\json1.c
-
-# Extension header files, part 1.
-#
-SRC08 = \
- $(TOP)\ext\fts1\fts1.h \
- $(TOP)\ext\fts1\fts1_hash.h \
- $(TOP)\ext\fts1\fts1_tokenizer.h \
- $(TOP)\ext\fts2\fts2.h \
- $(TOP)\ext\fts2\fts2_hash.h \
- $(TOP)\ext\fts2\fts2_tokenizer.h
+ $(TOP)\ext\misc\stmt.c
# Extension header files, part 2.
#
@@ -1218,6 +1528,7 @@ SRC09 = \
$(TOP)\ext\fts3\fts3_tokenizer.h \
$(TOP)\ext\icu\sqliteicu.h \
$(TOP)\ext\rtree\rtree.h \
+ $(TOP)\ext\rtree\geopoly.c \
$(TOP)\ext\rbu\sqlite3rbu.h \
$(TOP)\ext\session\sqlite3session.h
@@ -1233,11 +1544,22 @@ SRC11 = \
keywordhash.h \
opcodes.h \
parse.h \
+ shell.c \
$(SQLITE3H)
+# Generated Tcl header files
+#
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+SRC12 = \
+ $(SQLITETCLH) \
+ $(SQLITETCLDECLSH)
+!ELSE
+SRC12 =
+!ENDIF
+
# All source code files.
#
-SRC = $(SRC00) $(SRC01) $(SRC02) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11)
+SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC07) $(SRC09) $(SRC10) $(SRC11) $(SRC12)
# Source code to the test files.
#
@@ -1248,7 +1570,6 @@ TESTSRC = \
$(TOP)\src\test4.c \
$(TOP)\src\test5.c \
$(TOP)\src\test6.c \
- $(TOP)\src\test7.c \
$(TOP)\src\test8.c \
$(TOP)\src\test9.c \
$(TOP)\src\test_autoext.c \
@@ -1258,6 +1579,7 @@ TESTSRC = \
$(TOP)\src\test_blob.c \
$(TOP)\src\test_btree.c \
$(TOP)\src\test_config.c \
+ $(TOP)\src\test_delete.c \
$(TOP)\src\test_demovfs.c \
$(TOP)\src\test_devsym.c \
$(TOP)\src\test_fs.c \
@@ -1267,6 +1589,7 @@ TESTSRC = \
$(TOP)\src\test_intarray.c \
$(TOP)\src\test_journal.c \
$(TOP)\src\test_malloc.c \
+ $(TOP)\src\test_md5.c \
$(TOP)\src\test_multiplex.c \
$(TOP)\src\test_mutex.c \
$(TOP)\src\test_onefile.c \
@@ -1275,13 +1598,15 @@ TESTSRC = \
$(TOP)\src\test_quota.c \
$(TOP)\src\test_rtree.c \
$(TOP)\src\test_schema.c \
- $(TOP)\src\test_server.c \
$(TOP)\src\test_superlock.c \
$(TOP)\src\test_syscall.c \
+ $(TOP)\src\test_tclsh.c \
$(TOP)\src\test_tclvar.c \
$(TOP)\src\test_thread.c \
+ $(TOP)\src\test_vdbecov.c \
$(TOP)\src\test_vfs.c \
$(TOP)\src\test_windirent.c \
+ $(TOP)\src\test_window.c \
$(TOP)\src\test_wsd.c \
$(TOP)\ext\fts3\fts3_term.c \
$(TOP)\ext\fts3\fts3_test.c \
@@ -1291,24 +1616,51 @@ TESTSRC = \
# Statically linked extensions.
#
TESTEXT = \
+ $(TOP)\ext\expert\sqlite3expert.c \
+ $(TOP)\ext\expert\test_expert.c \
$(TOP)\ext\misc\amatch.c \
+ $(TOP)\ext\misc\appendvfs.c \
+ $(TOP)\ext\misc\basexx.c \
$(TOP)\ext\misc\carray.c \
+ $(TOP)\ext\misc\cksumvfs.c \
$(TOP)\ext\misc\closure.c \
$(TOP)\ext\misc\csv.c \
+ $(TOP)\ext\misc\decimal.c \
$(TOP)\ext\misc\eval.c \
+ $(TOP)\ext\misc\explain.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\fuzzer.c \
$(TOP)\ext\fts5\fts5_tcl.c \
$(TOP)\ext\fts5\fts5_test_mi.c \
$(TOP)\ext\fts5\fts5_test_tok.c \
$(TOP)\ext\misc\ieee754.c \
+ $(TOP)\ext\misc\mmapwarm.c \
$(TOP)\ext\misc\nextchar.c \
+ $(TOP)\ext\misc\normalize.c \
$(TOP)\ext\misc\percentile.c \
+ $(TOP)\ext\misc\prefixes.c \
+ $(TOP)\ext\misc\qpvtab.c \
+ $(TOP)\ext\misc\randomjson.c \
$(TOP)\ext\misc\regexp.c \
+ $(TOP)\ext\misc\remember.c \
$(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\spellfix.c \
+ $(TOP)\ext\misc\stmtrand.c \
$(TOP)\ext\misc\totype.c \
- $(TOP)\ext\misc\wholenumber.c
+ $(TOP)\ext\misc\unionvtab.c \
+ $(TOP)\ext\misc\wholenumber.c \
+ $(TOP)\ext\rtree\test_rtreedoc.c \
+ $(TOP)\ext\recover\sqlite3recover.c \
+ $(TOP)\ext\recover\test_recover.c \
+ $(TOP)\ext\intck\test_intck.c \
+ $(TOP)\ext\intck\sqlite3intck.c \
+ $(TOP)\ext\recover\dbdata.c
+
+# If use of zlib is enabled, add the "zipfile.c" source file.
+#
+!IF $(USE_ZLIB)!=0
+TESTEXT = $(TESTEXT) $(TOP)\ext\misc\zipfile.c
+!ENDIF
# Source code to the library files needed by the test fixture
# (non-amalgamation)
@@ -1316,10 +1668,10 @@ TESTEXT = \
TESTSRC2 = \
$(SRC00) \
$(SRC01) \
- $(SRC06) \
$(SRC07) \
$(SRC10) \
- $(TOP)\ext\async\sqlite3async.c
+ $(TOP)\ext\async\sqlite3async.c \
+ fts5.c
# Header files used by all library source files.
#
@@ -1341,7 +1693,7 @@ HDR = \
parse.h \
$(TOP)\src\pragma.h \
$(SQLITE3H) \
- $(TOP)\src\sqlite3ext.h \
+ sqlite3ext.h \
$(TOP)\src\sqliteInt.h \
$(TOP)\src\sqliteLimit.h \
$(TOP)\src\vdbe.h \
@@ -1351,21 +1703,14 @@ HDR = \
# Header files used by extensions
#
-EXTHDR = $(EXTHDR) \
- $(TOP)\ext\fts1\fts1.h \
- $(TOP)\ext\fts1\fts1_hash.h \
- $(TOP)\ext\fts1\fts1_tokenizer.h
-EXTHDR = $(EXTHDR) \
- $(TOP)\ext\fts2\fts2.h \
- $(TOP)\ext\fts2\fts2_hash.h \
- $(TOP)\ext\fts2\fts2_tokenizer.h
EXTHDR = $(EXTHDR) \
$(TOP)\ext\fts3\fts3.h \
$(TOP)\ext\fts3\fts3Int.h \
$(TOP)\ext\fts3\fts3_hash.h \
$(TOP)\ext\fts3\fts3_tokenizer.h
EXTHDR = $(EXTHDR) \
- $(TOP)\ext\rtree\rtree.h
+ $(TOP)\ext\rtree\rtree.h \
+ $(TOP)\ext\rtree\geopoly.c
EXTHDR = $(EXTHDR) \
$(TOP)\ext\icu\sqliteicu.h
EXTHDR = $(EXTHDR) \
@@ -1379,8 +1724,10 @@ TESTPROGS = \
testfixture.exe \
$(SQLITE3EXE) \
sqlite3_analyzer.exe \
+ sqlite3_checker.exe \
sqldiff.exe \
- dbhash.exe
+ dbhash.exe \
+ sqltclsh.exe
# Databases containing fuzzer test cases
#
@@ -1388,22 +1735,85 @@ FUZZDATA = \
$(TOP)\test\fuzzdata1.db \
$(TOP)\test\fuzzdata2.db \
$(TOP)\test\fuzzdata3.db \
- $(TOP)\test\fuzzdata4.db
+ $(TOP)\test\fuzzdata4.db \
+ $(TOP)\test\fuzzdata5.db \
+ $(TOP)\test\fuzzdata6.db \
+ $(TOP)\test\fuzzdata7.db \
+ $(TOP)\test\fuzzdata8.db
# <>
# Additional compiler options for the shell. These are only effective
# when the shell is not being dynamically linked.
#
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1
!ENDIF
# <>
# Extra compiler options for various test tools.
#
-MPTESTER_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS5
-FUZZERSHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1
-FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
+MPTESTER_COMPILE_OPTS = -DSQLITE_ENABLE_FTS5
+FUZZERSHELL_COMPILE_OPTS =
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -I$(TOP)\test -I$(TOP)\ext\recover
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_MEMSYS5
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OSS_FUZZ
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBPAGE_VTAB
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DESERIALIZE
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS3_PARENTHESIS
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS4
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS5
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_MATH_FUNCTIONS
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_MEMSYS5
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_NORMALIZE
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_PREUPDATE_HOOK
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_RTREE
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_SESSION
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STMTVTAB
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STAT4
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MEMORY=50000000
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MMAP_SIZE=0
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRINTF_PRECISION_LIMIT=1000
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRIVATE=""
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_STRICT_SUBTYPE=1
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_STATIC_RANDOMJSON
+
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_MAX_MEMORY=50000000
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_PRINTF_PRECISION_LIMIT=1000
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS4
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS5
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_RTREE
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB
+FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzcheck.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\ossfuzz.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\percentile.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c
+
+OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
+DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
+KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
+ST_COMPILE_OPTS = -DSQLITE_THREADSAFE=0
# Standard options to testfixture.
#
@@ -1412,7 +1822,7 @@ TESTOPTS = --verbose=file --output=test-out.txt
# Extra targets for the "all" target that require Tcl.
#
!IF $(NO_TCL)==0
-ALL_TCL_TARGETS = libtclsqlite3.lib
+ALL_TCL_TARGETS = $(SQLITE3TCLDLL)
!ELSE
ALL_TCL_TARGETS =
!ENDIF
@@ -1421,7 +1831,15 @@ ALL_TCL_TARGETS =
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
-all: dll libsqlite3.lib shell $(ALL_TCL_TARGETS)
+core: dll libsqlite3.lib shell
+
+# Targets that require the Tcl library.
+#
+tcl: $(ALL_TCL_TARGETS)
+
+# This Makefile target builds all of the standard binaries.
+#
+all: core tcl
# Dynamic link library section.
#
@@ -1436,7 +1854,35 @@ libsqlite3.lib: $(LIBOBJ)
$(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS)
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
- $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS)
+ $(LTLIB) $(LTLIBOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS)
+
+tclsqlite3.def: tclsqlite.lo
+ echo EXPORTS > tclsqlite3.def
+ dumpbin /all tclsqlite.lo \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+/EXPORT:_?((?:Sqlite3|Tclsqlite3)_[^@]*)(?:@\d+)?$$" \1 \
+ | sort >> tclsqlite3.def
+
+pkgIndex.tcl: $(TOP)\VERSION
+ for /F %%V in ('type "$(TOP)\VERSION"') do ( \
+ echo package ifneeded sqlite3 @version@ [list load [file join $$dir $(SQLITE3TCLDLL)] Sqlite3] \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact @version@ %%V > pkgIndex.tcl \
+ )
+
+$(SQLITE3TCLDLL): libtclsqlite3.lib $(LIBRESOBJS) tclsqlite3.def pkgIndex.tcl
+ $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:tclsqlite3.def /OUT:$@ libtclsqlite3.lib $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+
+tclextension: $(SQLITE3TCLDLL)
+
+tclextension-install: $(SQLITE3TCLDLL)
+ $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --install-only
+
+tclextension-uninstall:
+ $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --uninstall
+
+tclextension-list:
+ $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --info
+
+
# <>
$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
@@ -1446,35 +1892,70 @@ $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
sqlite3.def: libsqlite3.lib
echo EXPORTS > sqlite3.def
dumpbin /all libsqlite3.lib \
- | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3_[^@]*)(?:@\d+)?$$" \1 \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser|rbu)?_[^@]*)(?:@\d+)?$$" \1 \
| sort >> sqlite3.def
# <>
-$(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
- $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c $(SHELL_CORE_SRC) \
+$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
+ $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
# <>
-sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
+ $(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+RSYNC_SRC = \
+ $(TOP)\tool\sqlite3_rsync.c \
+ $(SQLITE3C)
+
+RSYNC_OPT = \
+ -DSQLITE_ENABLE_DBPAGE_VTAB \
+ -DSQLITE_THREADSAFE=0 \
+ -DSQLITE_OMIT_LOAD_EXTENSION \
+ -DSQLITE_OMIT_DEPRECATED
+
+sqlite3_rsync.exe: $(RSYNC_SRC) $(LIBRESOBJS)
+ $(LTLINK) $(RSYNC_OPT) $(NO_WARN) $(RSYNC_SRC) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
+
scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+ $(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
srcck1.exe: $(TOP)\tool\srcck1.c
$(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c
-sourcetest: srcck1.exe sqlite3.c
- srcck1.exe sqlite3.c
+sourcetest: srcck1.exe $(SQLITE3C)
+ srcck1.exe $(SQLITE3C)
+
+src-verify.exe: $(TOP)\tool\src-verify.c
+ $(LTLINK) $(NO_WARN) $(TOP)\tool\src-verify.c
+
+verify-source: src-verify.exe
+ src-verify.exe $(TOP)
fuzzershell.exe: $(TOP)\tool\fuzzershell.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) $(TOP)\tool\fuzzershell.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
-fuzzcheck.exe: $(TOP)\test\fuzzcheck.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(TOP)\test\fuzzcheck.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+dbfuzz.exe: $(TOP)\test\dbfuzz.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) $(DBFUZZ_COMPILE_OPTS) $(TOP)\test\dbfuzz.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
+fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) /F 8388608 $(NO_WARN) $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
+fuzzcheck-asan.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) /fsanitize=address $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
+run-fuzzcheck: fuzzcheck.exe fuzzcheck-asan.exe
+ fuzzcheck --spinner $(FUZZDB)
+ fuzzcheck-asan --spinner $(FUZZDB)
+
+ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) $(FUZZCHECK_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
+sessionfuzz.exe: zlib $(TOP)\test\sessionfuzz.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) -I$(ZLIBINCDIR) $(TOP)\test\sessionfuzz.c /link $(LDFLAGS) $(LTLINKOPTS) /LIBPATH:$(ZLIBLIBDIR) $(ZLIBLIB)
mptester.exe: $(TOP)\mptest\mptest.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(MPTESTER_COMPILE_OPTS) $(TOP)\mptest\mptest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
@@ -1499,32 +1980,30 @@ mptest: mptester.exe
# files are automatically generated. This target takes care of
# all that automatic generation.
#
-.target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl fts5.c
+.target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl fts5.c $(SQLITE_TCL_DEP)
-rmdir /Q/S tsrc 2>NUL
-mkdir tsrc
for %i in ($(SRC00)) do copy /Y %i tsrc
for %i in ($(SRC01)) do copy /Y %i tsrc
- for %i in ($(SRC02)) do copy /Y %i tsrc
for %i in ($(SRC03)) do copy /Y %i tsrc
for %i in ($(SRC04)) do copy /Y %i tsrc
for %i in ($(SRC05)) do copy /Y %i tsrc
- for %i in ($(SRC06)) do copy /Y %i tsrc
for %i in ($(SRC07)) do copy /Y %i tsrc
- for %i in ($(SRC08)) do copy /Y %i tsrc
for %i in ($(SRC09)) do copy /Y %i tsrc
for %i in ($(SRC10)) do copy /Y %i tsrc
for %i in ($(SRC11)) do copy /Y %i tsrc
+ for %i in ($(SRC12)) do copy /Y %i tsrc
copy /Y fts5.c tsrc
+ copy /B tsrc\fts5.c +,,
copy /Y fts5.h tsrc
+ copy /B tsrc\fts5.h +,,
del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL
$(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new
move vdbe.new tsrc\vdbe.c
echo > .target_source
-sqlite3.c: .target_source sqlite3ext.h $(TOP)\tool\mksqlite3c.tcl
- $(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
- copy tsrc\shell.c .
- copy $(TOP)\ext\session\sqlite3session.h .
+sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-verify.exe
+ $(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS) $(EXTRA_SRC)
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
@@ -1539,12 +2018,19 @@ sqlite3.lo: $(SQLITE3C)
# Rules to build the LEMON compiler generator
#
lempar.c: $(TOP)\tool\lempar.c
- copy $(TOP)\tool\lempar.c .
+ copy /Y $(TOP)\tool\lempar.c .
+ copy /B lempar.c +,,
lemon.exe: $(TOP)\tool\lemon.c lempar.c
$(BCC) $(NO_WARN) -Daccess=_access \
-Fe$@ $(TOP)\tool\lemon.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS)
+# <>
+# Rules to build the source-id generator tool
+#
+mksourceid.exe: $(TOP)\tool\mksourceid.c
+ $(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\mksourceid.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS)
+
# Rules to build individual *.lo files from generated *.c files. This
# applies to:
#
@@ -1562,7 +2048,7 @@ opcodes.lo: opcodes.c
#
!IF $(USE_RC)!=0
# <>
-$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(SQLITE3H)
+$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(SQLITE3H) $(TOP)\VERSION
echo #ifndef SQLITE_RESOURCE_VERSION > sqlite3rc.h
for /F %%V in ('type "$(TOP)\VERSION"') do ( \
echo #define SQLITE_RESOURCE_VERSION %%V \
@@ -1615,7 +2101,10 @@ ctime.lo: $(TOP)\src\ctime.c $(HDR)
date.lo: $(TOP)\src\date.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\date.c
-dbstat.lo: $(TOP)\src\date.c $(HDR)
+dbpage.lo: $(TOP)\src\dbpage.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\dbpage.c
+
+dbstat.lo: $(TOP)\src\dbstat.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\dbstat.c
delete.lo: $(TOP)\src\delete.c $(HDR)
@@ -1642,6 +2131,9 @@ hash.lo: $(TOP)\src\hash.c $(HDR)
insert.lo: $(TOP)\src\insert.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\insert.c
+json.lo: $(TOP)\src\json.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\json.c
+
legacy.lo: $(TOP)\src\legacy.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\legacy.c
@@ -1669,6 +2161,9 @@ mem3.lo: $(TOP)\src\mem3.c $(HDR)
mem5.lo: $(TOP)\src\mem5.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\mem5.c
+memdb.lo: $(TOP)\src\memdb.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\memdb.c
+
memjournal.lo: $(TOP)\src\memjournal.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\memjournal.c
@@ -1699,6 +2194,9 @@ pcache1.lo: $(TOP)\src\pcache1.c $(HDR) $(TOP)\src\pcache.h
os.lo: $(TOP)\src\os.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\os.c
+os_kv.lo: $(TOP)\src\os_kv.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\os_kv.c
+
os_unix.lo: $(TOP)\src\os_unix.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\os_unix.c
@@ -1747,6 +2245,9 @@ trigger.lo: $(TOP)\src\trigger.c $(HDR)
update.lo: $(TOP)\src\update.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\update.c
+upsert.lo: $(TOP)\src\upsert.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\upsert.c
+
utf.lo: $(TOP)\src\utf.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\utf.c
@@ -1777,6 +2278,9 @@ vdbesort.lo: $(TOP)\src\vdbesort.c $(HDR)
vdbetrace.lo: $(TOP)\src\vdbetrace.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vdbetrace.c
+vdbevtab.lo: $(TOP)\src\vdbevtab.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vdbevtab.c
+
vtab.lo: $(TOP)\src\vtab.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vtab.c
@@ -1795,14 +2299,17 @@ wherecode.lo: $(TOP)\src\wherecode.c $(HDR)
whereexpr.lo: $(TOP)\src\whereexpr.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\whereexpr.c
-tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR)
+window.lo: $(TOP)\src\window.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\window.c
+
+tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
-tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR)
- $(LTCOMPILE) $(NO_WARN) -DTCLSH=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
+tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
+ $(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
- $(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+ $(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
# Rules to build opcodes.c and opcodes.h
#
@@ -1816,18 +2323,28 @@ opcodes.h: parse.h $(TOP)\src\vdbe.c $(TOP)\tool\mkopcodeh.tcl
#
parse.h: parse.c
-parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl
+parse.c: $(TOP)\src\parse.y lemon.exe
del /Q parse.y parse.h parse.h.temp 2>NUL
- copy $(TOP)\src\parse.y .
- .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) parse.y
- move parse.h parse.h.temp
- $(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h
+ copy /Y $(TOP)\src\parse.y .
+ copy /B parse.y +,,
+ .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S parse.y
-$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
- $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H)
+$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION
+ $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS)
sqlite3ext.h: .target_source
- copy tsrc\sqlite3ext.h .
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+ type tsrc\sqlite3ext.h | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*\)" "(SQLITE_CALLBACK *)" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*" "(SQLITE_APICALL *" > sqlite3ext.h
+ copy /Y sqlite3ext.h tsrc\sqlite3ext.h
+!ELSE
+ copy /Y tsrc\sqlite3ext.h sqlite3ext.h
+ copy /B sqlite3ext.h +,,
+!ENDIF
+
+sqlite3session.h: $(TOP)\ext\session\sqlite3session.h
+ copy /Y $(TOP)\ext\session\sqlite3session.h .
+ copy /B sqlite3session.h +,,
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
$(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) \
@@ -1836,31 +2353,57 @@ mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
.\mkkeywordhash.exe > keywordhash.h
-
+# Source and header files that shell.c depends on
+SHELL_DEP = \
+ $(TOP)\src\shell.c.in \
+ $(TOP)\ext\expert\sqlite3expert.c \
+ $(TOP)\ext\expert\sqlite3expert.h \
+ $(TOP)\ext\intck\sqlite3intck.c \
+ $(TOP)\ext\intck\sqlite3intck.h \
+ $(TOP)\ext\misc\appendvfs.c \
+ $(TOP)\ext\misc\base64.c \
+ $(TOP)\ext\misc\base85.c \
+ $(TOP)\ext\misc\completion.c \
+ $(TOP)\ext\misc\decimal.c \
+ $(TOP)\ext\misc\fileio.c \
+ $(TOP)\ext\misc\ieee754.c \
+ $(TOP)\ext\misc\memtrace.c \
+ $(TOP)\ext\misc\pcachetrace.c \
+ $(TOP)\ext\misc\percentile.c \
+ $(TOP)\ext\misc\regexp.c \
+ $(TOP)\ext\misc\series.c \
+ $(TOP)\ext\misc\sha1.c \
+ $(TOP)\ext\misc\shathree.c \
+ $(TOP)\ext\misc\sqlar.c \
+ $(TOP)\ext\misc\sqlite3_stdio.c \
+ $(TOP)\ext\misc\sqlite3_stdio.h \
+ $(TOP)\ext\misc\uint.c \
+ $(TOP)\ext\misc\vfstrace.c \
+ $(TOP)\ext\misc\zipfile.c \
+ $(TOP)\ext\recover\dbdata.c \
+ $(TOP)\ext\recover\sqlite3recover.c \
+ $(TOP)\ext\recover\sqlite3recover.h \
+ $(TOP)\src\test_windirent.c \
+ $(TOP)\src\test_windirent.h
+
+# If use of zlib is enabled, add the "zipfile.c" source file.
+#
+!IF $(USE_ZLIB)!=0
+SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\sqlar.c
+SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\zipfile.c
+!ENDIF
+
+shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl
+ $(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl > shell.c
+
+zlib:
+ pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd
# Rules to build the extension objects.
#
icu.lo: $(TOP)\ext\icu\icu.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\icu\icu.c
-fts2.lo: $(TOP)\ext\fts2\fts2.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2.c
-
-fts2_hash.lo: $(TOP)\ext\fts2\fts2_hash.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_hash.c
-
-fts2_icu.lo: $(TOP)\ext\fts2\fts2_icu.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_icu.c
-
-fts2_porter.lo: $(TOP)\ext\fts2\fts2_porter.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_porter.c
-
-fts2_tokenizer.lo: $(TOP)\ext\fts2\fts2_tokenizer.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_tokenizer.c
-
-fts2_tokenizer1.lo: $(TOP)\ext\fts2\fts2_tokenizer1.c $(HDR) $(EXTHDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_tokenizer1.c
-
fts3.lo: $(TOP)\ext\fts3\fts3.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3.c
@@ -1900,6 +2443,9 @@ fts3_unicode2.lo: $(TOP)\ext\fts3\fts3_unicode2.c $(HDR) $(EXTHDR)
fts3_write.lo: $(TOP)\ext\fts3\fts3_write.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_write.c
+stmt.lo: $(TOP)\ext\misc\stmt.c $(HDR) $(EXTHDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\misc\stmt.c
+
rtree.lo: $(TOP)\ext\rtree\rtree.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\rtree\rtree.c
@@ -1925,16 +2471,41 @@ FTS5_SRC = \
$(TOP)\ext\fts5\fts5_varint.c \
$(TOP)\ext\fts5\fts5_vocab.c
+LSM1_SRC = \
+ $(TOP)\ext\lsm1\lsm.h \
+ $(TOP)\ext\lsm1\lsmInt.h \
+ $(TOP)\ext\lsm1\lsm_ckpt.c \
+ $(TOP)\ext\lsm1\lsm_file.c \
+ $(TOP)\ext\lsm1\lsm_log.c \
+ $(TOP)\ext\lsm1\lsm_main.c \
+ $(TOP)\ext\lsm1\lsm_mem.c \
+ $(TOP)\ext\lsm1\lsm_mutex.c \
+ $(TOP)\ext\lsm1\lsm_shared.c \
+ $(TOP)\ext\lsm1\lsm_sorted.c \
+ $(TOP)\ext\lsm1\lsm_str.c \
+ $(TOP)\ext\lsm1\lsm_tree.c \
+ $(TOP)\ext\lsm1\lsm_unix.c \
+ $(TOP)\ext\lsm1\lsm_varint.c \
+ $(TOP)\ext\lsm1\lsm_vtab.c \
+ $(TOP)\ext\lsm1\lsm_win32.c
+
fts5parse.c: $(TOP)\ext\fts5\fts5parse.y lemon.exe
- copy $(TOP)\ext\fts5\fts5parse.y .
+ copy /Y $(TOP)\ext\fts5\fts5parse.y .
+ copy /B fts5parse.y +,,
del /Q fts5parse.h 2>NUL
- .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) fts5parse.y
+ .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S fts5parse.y
fts5parse.h: fts5parse.c
fts5.c: $(FTS5_SRC)
$(TCLSH_CMD) $(TOP)\ext\fts5\tool\mkfts5c.tcl
- copy $(TOP)\ext\fts5\fts5.h .
+ copy /Y $(TOP)\ext\fts5\fts5.h .
+ copy /B fts5.h +,,
+
+lsm1.c: $(LSM1_SRC)
+ $(TCLSH_CMD) $(TOP)\ext\lsm1\tool\mklsm1c.tcl
+ copy /Y $(TOP)\ext\lsm1\lsm.h .
+ copy /B lsm.h +,,
fts5.lo: fts5.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c fts5.c
@@ -1955,11 +2526,18 @@ sqlite3rbu.lo: $(TOP)\ext\rbu\sqlite3rbu.c $(HDR) $(EXTHDR)
# necessary because the test fixture requires non-API symbols which are
# hidden when the library is built via the amalgamation).
#
-TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
+TESTFIXTURE_FLAGS = -DTCLSH_INIT_PROC=sqlite3TestInit -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CKSUMVFS_STATIC=1
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STATIC_RANDOMJSON
+TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STRICT_SUBTYPE=1
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
@@ -1969,17 +2547,46 @@ TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
!ENDIF
-testfixture.exe: $(TESTFIXTURE_SRC) $(SQLITE3H) $(LIBRESOBJS) $(HDR)
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+sqlite_tclDecls.h:
+ echo #ifndef SQLITE_TCLAPI > $(SQLITETCLDECLSH)
+ echo # define SQLITE_TCLAPI >> $(SQLITETCLDECLSH)
+ echo #endif >> $(SQLITETCLDECLSH)
+ type "$(TCLINCDIR)\tclDecls.h" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "^(EXTERN(?: CONST\d+?)?\s+?[^\(]*?\s+?)Tcl_" "\1 SQLITE_TCLAPI Tcl_" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "^(EXTERN\s+?(?:void|VOID)\s+?)TclFreeObj" "\1 SQLITE_TCLAPI TclFreeObj" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*tcl_" "(SQLITE_TCLAPI *tcl_" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*tclFreeObj" "(SQLITE_TCLAPI *tclFreeObj" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "\(\*" "(SQLITE_TCLAPI *" >> $(SQLITETCLDECLSH)
+
+sqlite_tcl.h:
+ type "$(TCLINCDIR)\tcl.h" | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact tclDecls.h sqlite_tclDecls.h \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl regsub "typedef (.*?)\(Tcl_" "typedef \1 (SQLITE_TCLAPI Tcl_" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "void (*freeProc)" "void (SQLITE_TCLAPI *freeProc)" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "Tcl_HashEntry *(*findProc)" "Tcl_HashEntry *(SQLITE_TCLAPI *findProc)" \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "Tcl_HashEntry *(*createProc)" "Tcl_HashEntry *(SQLITE_TCLAPI *createProc)" >> $(SQLITETCLH)
+!ENDIF
+
+testfixture.exe: $(TESTFIXTURE_SRC) $(TESTFIXTURE_DEP) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
-DBUILD_sqlite -I$(TCLINCDIR) \
$(TESTFIXTURE_SRC) \
- /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+ /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
extensiontest: testfixture.exe testloadext.dll
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS)
-fulltest: $(TESTPROGS) fuzztest
+tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe sqlite3_rsync.exe $(TOP)\tool\mktoolzip.tcl
+ .\testfixture.exe $(TOP)\tool\mktoolzip.tcl
+
+coretestprogs: testfixture.exe sqlite3.exe
+
+testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe
+
+fulltest: alltest fuzztest
+
+alltest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\all.test $(TESTOPTS)
@@ -1998,8 +2605,12 @@ queryplantest: testfixture.exe shell
fuzztest: fuzzcheck.exe
.\fuzzcheck.exe $(FUZZDATA)
-fastfuzztest: fuzzcheck.exe
- .\fuzzcheck.exe --limit-mem 100M $(FUZZDATA)
+# Legacy testing target for third-party integrators. The SQLite
+# developers seldom use this target themselves. Instead
+# they use "nmake /f Makefile.msc devtest" which runs tests on
+# a standard set of options
+#
+test: $(TESTPROGS) sourcetest fuzztest tcltest
# Minimal testing that runs in less than 3 minutes (on a fast machine)
#
@@ -2010,76 +2621,184 @@ quicktest: testfixture.exe sourcetest
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
-test: $(TESTPROGS) sourcetest fastfuzztest
+
+# The veryquick.test TCL tests.
+#
+tcltest: testfixture.exe
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\veryquick.test $(TESTOPTS)
+# Runs all the same tests cases as the "tcltest" target but uses
+# the testrunner.tcl script to run them in multiple cores
+# concurrently.
+testrunner: testfixture.exe
+ .\testfixture.exe $(TOP)\test\testrunner.tcl
+
+# This is the testing target preferred by the core SQLite developers.
+# It runs tests under a standard configuration. The devs run
+# "nmake /f Makefile.msc devtest" prior to each check-in, at a minimum.
+# Probably other tests too, but at least this one.
+#
+devtest: srctree-check sourcetest
+ $(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
+
+mdevtest:
+ $(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
+
+# Validate that various generated files in the source tree
+# are up-to-date.
+#
+srctree-check: $(TOP)\tool\srctree-check.tcl
+ $(TCLSH_CMD) $(TOP)\tool\srctree-check.tcl
+
+# Testing for a release
+#
+releasetest:
+ $(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
+
+
smoketest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
-sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
- echo #define TCLSH 2 > $@
- echo #define SQLITE_ENABLE_DBSTAT_VTAB 1 >> $@
- copy $@ + $(SQLITE3C) + $(TOP)\src\tclsqlite.c $@
- echo static const char *tclsh_main_loop(void){ >> $@
- echo static const char *zMainloop = >> $@
- $(TCLSH_CMD) $(TOP)\tool\tostr.tcl $(TOP)\tool\spaceanal.tcl >> $@
- echo ; return zMainloop; } >> $@
+shelltest: $(TESTPROGS)
+ .\testfixture.exe $(TOP)\test\permutations.test shell
+
+sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
+ $(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in > $@
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
- /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+ /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
-testloadext.lo: $(TOP)\src\test_loadext.c
+sqltclsh.c: sqlite3.c $(TOP)\src\tclsqlite.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
+ $(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in >sqltclsh.c
+
+sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
+ $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqltclsh.c \
+ /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
+
+sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
+ $(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
+
+CHECKER_DEPS =\
+ $(TOP)\tool\mkccode.tcl \
+ sqlite3.c \
+ $(TOP)\src\tclsqlite.c \
+ $(TOP)\ext\repair\sqlite3_checker.tcl \
+ $(TOP)\ext\repair\checkindex.c \
+ $(TOP)\ext\repair\checkfreelist.c \
+ $(TOP)\ext\misc\btreeinfo.c \
+ $(TOP)\ext\repair\sqlite3_checker.c.in
+
+sqlite3_checker.c: $(CHECKER_DEPS)
+ $(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\ext\repair\sqlite3_checker.c.in > $@
+
+sqlite3_checker.exe: sqlite3_checker.c $(LIBRESOBJS)
+ $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_checker.c \
+ /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
+
+dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
+ $(LTLINK) $(NO_WARN) -DDBDUMP_STANDALONE $(TOP)\ext\misc\dbdump.c $(SQLITE3C) \
+ /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS)
+
+testloadext.lo: $(TOP)\src\test_loadext.c $(SQLITE3H)
$(LTCOMPILE) $(NO_WARN) -c $(TOP)\src\test_loadext.c
testloadext.dll: testloadext.lo
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo
+dbtotxt.exe: $(TOP)\tool\dbtotxt.c
+ $(LTLINK) $(NO_WARN) $(TOP)\tool\dbtotxt.c /link $(LDFLAGS) $(LTLINKOPTS)
+
showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\showdb.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
showstat4.exe: $(TOP)\tool\showstat4.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\showstat4.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
showjournal.exe: $(TOP)\tool\showjournal.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\showjournal.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\showwal.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
-changeset.exe: $(TOP)\ext\session\changeset.c $(SQLITE3C)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+showshm.exe: $(TOP)\tool\showshm.c
+ $(LTLINK) $(NO_WARN) $(TOP)\tool\showshm.c /link $(LDFLAGS) $(LTLINKOPTS)
+
+index_usage.exe: $(TOP)\tool\index_usage.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+ $(TOP)\tool\index_usage.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
+changeset.exe: $(TOP)\ext\session\changeset.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+ -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 \
$(TOP)\ext\session\changeset.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+changesetfuzz.exe: $(TOP)\ext\session\changesetfuzz.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+ -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 \
+ $(TOP)\ext\session\changesetfuzz.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
fts3view.exe: $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
rollback-test.exe: $(TOP)\tool\rollback-test.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\rollback-test.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+atrc.exe: $(TOP)\test\atrc.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+ $(TOP)\test\atrc.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
LogEst.exe: $(TOP)\tool\logest.c $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -Fe$@ $(TOP)\tool\LogEst.c /link $(LDFLAGS) $(LTLINKOPTS)
+ $(LTLINK) $(NO_WARN) $(TOP)\tool\LogEst.c /link $(LDFLAGS) $(LTLINKOPTS)
wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\test\wordcount.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+kvtest.exe: $(TOP)\test\kvtest.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) $(KV_COMPILE_OPTS) \
+ $(TOP)\test\kvtest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H)
- $(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU -Fe$@ \
+ $(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \
$(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+THREADTEST3_SRC = \
+ $(TOP)\test\threadtest3.c \
+ $(TOP)\test\tt3_checkpoint.c \
+ $(TOP)\test\tt3_index.c \
+ $(TOP)\test\tt3_vacuum.c \
+ $(TOP)\test\tt3_stress.c \
+ $(TOP)\test\tt3_lookaside1.c
+
+threadtest3.exe: $(THREADTEST3_SRC) $(TOP)\src\test_multiplex.c $(SQLITE3C) $(SQLITE3H)
+ $(LTLINK) $(NO_WARN) $(TOP)\test\threadtest3.c $(TOP)\src\test_multiplex.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+
+# Display key variables that control which version of TCL is to be used.
+#
+tcl-env:
+ @echo TCLDIR = $(TCLDIR)
+ @echo TCLVERSION = $(TCLVERSION)
+ @echo TCLSUFFIX = $(TCLSUFFIX)
+ @echo LIBTCL = $(LIBTCL)
+ @echo LIBTCLSTUB = $(LIBTCLSTUB)
+ @echo TCLSH_CMD = $(TCLSH_CMD)
+
+LSMDIR=$(TOP)\ext\lsm1
+!INCLUDE $(LSMDIR)\Makefile.msc
+
moreclean: clean
del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL
# <>
@@ -2089,27 +2808,35 @@ clean:
del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL
del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL
# <>
- del /Q sqlite3.c sqlite3.h 2>NUL
+ del /Q $(SQLITE3TCLDLL) pkgIndex.tcl 2>NUL
del /Q opcodes.c opcodes.h 2>NUL
del /Q lemon.* lempar.c parse.* 2>NUL
- del /Q mkkeywordhash.* keywordhash.h 2>NUL
+ del /Q mksourceid.* mkkeywordhash.* keywordhash.h 2>NUL
del /Q notasharedlib.* 2>NUL
-rmdir /Q/S .deps 2>NUL
-rmdir /Q/S .libs 2>NUL
-rmdir /Q/S tsrc 2>NUL
del /Q .target_source 2>NUL
- del /Q tclsqlite3.exe 2>NUL
+ del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 2>NUL
+ del /Q lsm.dll lsmtest.exe 2>NUL
+ del /Q atrc.exe changesetfuzz.exe dbtotxt.exe index_usage.exe 2>NUL
del /Q testloadext.dll 2>NUL
del /Q testfixture.exe test.db 2>NUL
- del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe 2>NUL
+ del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL
del /Q changeset.exe 2>NUL
del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL
del /Q mptester.exe wordcount.exe rbu.exe srcck1.exe 2>NUL
- del /Q sqlite3.c sqlite3-*.c 2>NUL
+ del /Q sqlite3.c sqlite3-*.c sqlite3.h 2>NUL
del /Q sqlite3rc.h 2>NUL
del /Q shell.c sqlite3ext.h sqlite3session.h 2>NUL
- del /Q sqlite3_analyzer.exe sqlite3_analyzer.c 2>NUL
+ del /Q sqlite3_analyzer.exe sqlite3_analyzer.c sqlite3_rsync.exe 2>NUL
del /Q sqlite-*-output.vsix 2>NUL
del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe dbhash.exe 2>NUL
+ del /Q sqltclsh.* 2>NUL
+ del /Q dbfuzz.exe sessionfuzz.exe threadtest3.exe 2>NUL
+ del /Q kvtest.exe ossshell.exe scrub.exe 2>NUL
+ del /Q showshm.exe sqlite3_checker.* sqlite3_expert.exe 2>NUL
del /Q fts5.* fts5parse.* 2>NUL
+ del /Q lsm.h lsm1.c 2>NUL
+ del /q src-verify.exe 2>NUL
# <>
diff --git a/sqlite/README.md b/sqlite/README.md
index dbc02057..689b52da 100644
--- a/sqlite/README.md
+++ b/sqlite/README.md
@@ -1,14 +1,102 @@
SQLite Source Repository
-This repository contains the complete source code for the SQLite database
-engine. Some test scripts are also include. However, many other test scripts
+This repository contains the complete source code for the
+[SQLite database engine](https://sqlite.org/), including
+many test scripts. However, other test scripts
and most of the documentation are managed separately.
-If you are reading this on a Git mirror someplace, you are doing it wrong.
-The [official repository](https://www.sqlite.org/src/) is better. Go there
-now.
+See the [on-line documentation](https://sqlite.org/) for more information
+about what SQLite is and how it works from a user's perspective. This
+README file is about the source code that goes into building SQLite,
+not about how SQLite is used.
-## Compiling
+## Version Control
+
+SQLite sources are managed using
+[Fossil](https://fossil-scm.org/), a distributed version control system
+that was specifically designed and written to support SQLite development.
+The [Fossil repository](https://sqlite.org/src/timeline) contains the urtext.
+
+If you are reading this on GitHub or some other Git repository or service,
+then you are looking at a mirror. The names of check-ins and
+other artifacts in a Git mirror are different from the official
+names for those objects. The official names for check-ins are
+found in a footer on the check-in comment for authorized mirrors.
+The official check-in name can also be seen in the `manifest.uuid` file
+in the root of the tree. Always use the official name, not the
+Git-name, when communicating about an SQLite check-in.
+
+If you pulled your SQLite source code from a secondary source and want to
+verify its integrity, there are hints on how to do that in the
+[Verifying Code Authenticity](#vauth) section below.
+
+## Contacting The SQLite Developers
+
+The preferred way to ask questions or make comments about SQLite or to
+report bugs against SQLite is to visit the
+[SQLite Forum](https://sqlite.org/forum) at .
+Anonymous postings are permitted.
+
+If you think you have found a bug that has security implications and
+you do not want to report it on the public forum, you can send a private
+email to drh at sqlite dot org.
+
+## Public Domain
+
+The SQLite source code is in the public domain. See
+ for details.
+
+Because SQLite is in the public domain, we do not normally accept pull
+requests, because if we did take a pull request, the changes in that
+pull request might carry a copyright and the SQLite source code would
+then no longer be fully in the public domain.
+
+## Obtaining The SQLite Source Code
+
+If you do not want to use Fossil, you can download tarballs or ZIP
+archives or [SQLite archives](https://sqlite.org/cli.html#sqlar) as follows:
+
+ * Latest trunk check-in as
+ [Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz),
+ [ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip), or
+ [SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar).
+
+ * Latest release as
+ [Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=release),
+ [ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip?r=release), or
+ [SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar?r=release).
+
+ * For other check-ins, substitute an appropriate branch name or
+ tag or hash prefix in place of "release" in the URLs of the previous
+ bullet. Or browse the [timeline](https://www.sqlite.org/src/timeline)
+ to locate the check-in desired, click on its information page link,
+ then click on the "Tarball" or "ZIP Archive" links on the information
+ page.
+
+To access sources directly using [Fossil](https://fossil-scm.org/home),
+first install Fossil version 2.0 or later.
+Source tarballs and precompiled binaries available at
+. Fossil is
+a stand-alone program. To install, simply download or build the single
+executable file and put that file someplace on your $PATH.
+Then run commands like this:
+
+ mkdir -p ~/sqlite
+ cd ~/sqlite
+ fossil open https://sqlite.org/src
+
+The "fossil open" command will take two or three minutes. Afterwards,
+you can do fast, bandwidth-efficient updates to the whatever versions
+of SQLite you like. Some examples:
+
+ fossil update trunk ;# latest trunk check-in
+ fossil update release ;# latest official release
+ fossil update trunk:2024-01-01 ;# First trunk check-in after 2024-01-01
+ fossil update version-3.39.0 ;# Version 3.39.0
+
+Or type "fossil ui" to get a web-based user interface.
+
+## Compiling for Unix-like systems
First create a directory in which to place
the build products. It is recommended, but not required, that the
@@ -18,15 +106,42 @@ script found at the root of the source tree. Then run "make".
For example:
- tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
- mkdir bld ;# Build will occur in a sibling directory
- cd bld ;# Change to the build directory
- ../sqlite/configure ;# Run the configure script
- make ;# Run the makefile.
- make sqlite3.c ;# Build the "amalgamation" source file
- make test ;# Run some tests (requires Tcl)
+ apt install gcc make tcl-dev ;# Make sure you have all the necessary build tools
+ tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
+ mkdir bld ;# Build will occur in a sibling directory
+ cd bld ;# Change to the build directory
+ ../sqlite/configure ;# Run the configure script
+ make sqlite3 ;# Builds the "sqlite3" command-line tool
+ make sqlite3.c ;# Build the "amalgamation" source file
+ make sqldiff ;# Builds the "sqldiff" command-line tool
+ # Makefile targets below this point require tcl-dev
+ make tclextension-install ;# Build and install the SQLite TCL extension
+ make devtest ;# Run development tests
+ make releasetest ;# Run full release tests
+ make sqlite3_analyzer ;# Builds the "sqlite3_analyzer" tool
+
+See the makefile for additional targets. For debugging builds, the
+core developers typically run "configure" with options like this:
-See the makefile for additional targets.
+ ../sqlite/configure --enable-all --enable-debug CFLAGS='-O0 -g'
+
+For release builds, the core developers usually do:
+
+ ../sqlite/configure --enable-all
+
+Almost all makefile targets require a "tclsh" TCL interpreter version 8.6 or
+later. The "tclextension-install" target and the test targets that follow
+all require TCL development libraries too. ("apt install tcl-dev"). It is
+helpful, but is not required, to install the SQLite TCL extension (the
+"tclextension-install" target) prior to running tests. The "releasetest"
+target has additional requiremenst, such as "valgrind".
+
+On "make" command-lines, one can add "OPTIONS=..." to specify additional
+compile-time options over and above those set by ./configure. For example,
+to compile with the SQLITE_OMIT_DEPRECATED compile-time option, one could say:
+
+ ./configure --enable-all
+ make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
The configure script uses autoconf 2.61 and libtool. If the configure
script does not work out for you, there is a generic makefile named
@@ -34,58 +149,85 @@ script does not work out for you, there is a generic makefile named
can copy and edit to suit your needs. Comments on the generic makefile
show what changes are needed.
-## Using MSVC
-
-On Windows, all applicable build products can be compiled with MSVC.
-First open the command prompt window associated with the desired compiler
-version (e.g. "Developer Command Prompt for VS2013"). Next, use NMAKE
-with the provided "Makefile.msc" to build one of the supported targets.
-
-For example:
-
- mkdir bld
- cd bld
- nmake /f Makefile.msc TOP=..\sqlite
- nmake /f Makefile.msc sqlite3.c TOP=..\sqlite
- nmake /f Makefile.msc sqlite3.dll TOP=..\sqlite
- nmake /f Makefile.msc sqlite3.exe TOP=..\sqlite
- nmake /f Makefile.msc test TOP=..\sqlite
-
-There are several build options that can be set via the NMAKE command
-line. For example, to build for WinRT, simply add "FOR_WINRT=1" argument
-to the "sqlite3.dll" command line above. When debugging into the SQLite
-code, adding the "DEBUG=1" argument to one of the above command lines is
-recommended.
-
-SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation
-is required by the makefiles (including those for MSVC). SQLite contains
-a lot of generated code and Tcl is used to do much of that code generation.
-The makefiles also require AWK.
-
-## Source Code Tour
-
-Most of the core source files are in the **src/** subdirectory. But
-src/ also contains files used to build the "testfixture" test harness;
-those file all begin with "test". And src/ contains the "shell.c" file
-which is the main program for the "sqlite3.exe" command-line shell and
-the "tclsqlite.c" file which implements the bindings to SQLite from the
-Tcl programming language. (Historical note: SQLite began as a Tcl
-extension and only later escaped to the wild as an independent library.)
-
-Test scripts and programs are found in the **test/** subdirectory.
-There are other test suites for SQLite (see
-[How SQLite Is Tested](http://www.sqlite.org/testing.html))
-but those other test suites are
-in separate source repositories.
-
-The **ext/** subdirectory contains code for extensions. The
-Full-text search engine is in **ext/fts3**. The R-Tree engine is in
-**ext/rtree**. The **ext/misc** subdirectory contains a number of
-smaller, single-file extensions, such as a REGEXP operator.
-
-The **tool/** subdirectory contains various scripts and programs used
-for building generated source code files or for testing or for generating
-accessory programs such as "sqlite3_analyzer(.exe)".
+## Compiling for Windows Using MSVC
+
+On Windows, everything can be compiled with MSVC.
+You will also need a working installation of TCL.
+See the [compile-for-windows.md](doc/compile-for-windows.md) document for
+additional information about how to install MSVC and TCL and configure your
+build environment.
+
+If you want to run tests, you need to let SQLite know the location of your
+TCL library, using a command like this:
+
+ set TCLDIR=c:\Tcl
+
+SQLite uses "tclsh.exe" as part of the build process, and so that
+program will need to be somewhere on your %PATH%. SQLite itself
+does not contain any TCL code, but it does use TCL to help with the
+build process and to run tests. You may need to install TCL development
+libraries in order to successfully complete some makefile targets.
+It is helpful, but is not required, to install the SQLite TCL extension
+(the "tclextension-install" target) prior to running tests.
+
+Build using Makefile.msc. Example:
+
+ nmake /f Makefile.msc sqlite3.exe
+ nmake /f Makefile.msc sqlite3.c
+ nmake /f Makefile.msc sqldiff.exe
+ # Makefile targets below this point require TCL development libraries
+ nmake /f Makefile.msc tclextension-install
+ nmake /f Makefile.msc devtest
+ nmake /f Makefile.msc releasetest
+ nmake /f Makefile.msc sqlite3_analyzer.exe
+
+There are many other makefile targets. See comments in Makefile.msc for
+details.
+
+As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
+command-line to enable new compile-time options. For example:
+
+ nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
+
+## Source Tree Map
+
+ * **src/** - This directory contains the primary source code for the
+ SQLite core. For historical reasons, C-code used for testing is
+ also found here. Source files intended for testing begin with "`test`".
+ The `tclsqlite3.c` and `tclsqlite3.h` files are the TCL interface
+ for SQLite and are also not part of the core.
+
+ * **test/** - This directory and its subdirectories contains code used
+ for testing. Files that end in "`.test`" are TCL scripts that run
+ tests using an augmented TCL interpreter named "testfixture". Use
+ a command like "`make testfixture`" (unix) or
+ "`nmake /f Makefile.msc testfixture.exe`" (windows) to build that
+ augmented TCL interpreter, then run individual tests using commands like
+ "`testfixture test/main.test`". This test/ subdirectory also contains
+ additional C code modules and scripts for other kinds of testing.
+
+ * **tool/** - This directory contains programs and scripts used to
+ build some of the machine-generated code that goes into the SQLite
+ core, as well as to build and run tests and perform diagnostics.
+ The source code to [the Lemon parser generator](./doc/lemon.html) is
+ found here. There are also TCL scripts used to build and/or transform
+ source code files. For example, the tool/mksqlite3h.tcl script reads
+ the src/sqlite.h.in file and uses it as a template to construct
+ the deliverable "sqlite3.h" file that defines the SQLite interface.
+
+ * **ext/** - Various extensions to SQLite are found under this
+ directory. For example, the FTS5 subsystem is in "ext/fts5/".
+ Some of these extensions (ex: FTS3/4, FTS5, RTREE) might get built
+ into the SQLite amalgamation, but not all of them. The
+ "ext/misc/" subdirectory contains an assortment of one-file extensions,
+ many of which are omitted from the SQLite core, but which are included
+ in the [SQLite CLI](https://sqlite.org/cli.html).
+
+ * **doc/** - Some documentation files about SQLite internals are found
+ here. Note, however, that the primary documentation designed for
+ application developers and users of SQLite is in a completely separate
+ repository. Note also that the primary API documentation is derived
+ from specially constructed comments in the src/sqlite.h.in file.
### Generated Source Code Files
@@ -99,8 +241,8 @@ manually-edited files and automatically-generated files.
The SQLite interface is defined by the **sqlite3.h** header file, which is
generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The
-[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion.
-The manifest.uuid file contains the SHA1 hash of the particular check-in
+[Tcl script](https://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion.
+The manifest.uuid file contains the SHA3 hash of the particular check-in
and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file
contains the current SQLite version number. The sqlite3.h header is really
just a copy of src/sqlite.h.in with the source-id and version number inserted
@@ -108,23 +250,19 @@ at just the right spots. Note that comment text in the sqlite3.h file is
used to generate much of the SQLite API documentation. The Tcl scripts
used to generate that documentation are in a separate source repository.
-The SQL language parser is **parse.c** which is generate from a grammar in
+The SQL language parser is **parse.c** which is generated from a grammar in
the src/parse.y file. The conversion of "parse.y" into "parse.c" is done
by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code
-for lemon is at tool/lemon.c. Lemon uses a
-template for generating its parser. A generic template is in tool/lempar.c,
-but SQLite uses a slightly modified template found in src/lempar.c.
-
+for lemon is at tool/lemon.c. Lemon uses the tool/lempar.c file as a
+template for generating its parser.
Lemon also generates the **parse.h** header file, at the same time it
-generates parse.c. But the parse.h header file is
-modified further (to add additional symbols) using the ./addopcodes.awk
-AWK script.
+generates parse.c.
The **opcodes.h** header file contains macros that define the numbers
corresponding to opcodes in the "VDBE" virtual machine. The opcodes.h
-file is generated by the scanning the src/vdbe.c source file. The
-AWK script at ./mkopcodeh.awk does this scan and generates opcodes.h.
-A second AWK script, ./mkopcodec.awk, then scans opcodes.h to generate
+file is generated by scanning the src/vdbe.c source file. The
+Tcl script at ./mkopcodeh.tcl does this scan and generates opcodes.h.
+A second Tcl script, ./mkopcodec.tcl, then scans opcodes.h to generate
the **opcodes.c** source file, which contains a reverse mapping from
opcode-number to opcode-name that is used for EXPLAIN output.
@@ -133,6 +271,13 @@ that maps SQL language keywords (ex: "CREATE", "SELECT", "INDEX", etc.) into
the numeric codes used by the parse.c parser. The keywordhash.h file is
generated by a C-language program at tool mkkeywordhash.c.
+The **pragma.h** header file contains various definitions used to parse
+and implement the PRAGMA statements. The header is generated by a
+script **tool/mkpragmatab.tcl**. If you want to add a new PRAGMA, edit
+the **tool/mkpragmatab.tcl** file to insert the information needed by the
+parser for your new PRAGMA, then run the script to regenerate the
+**pragma.h** header file.
+
### The Amalgamation
All of the individual C source code and header files (both manually-edited
@@ -150,12 +295,12 @@ subdirectory (using the equivalent of "make target_source") then the
tool/mksqlite3c.tcl script is run to copy them all together in just the
right order while resolving internal "#include" references.
-The amalgamation source file is more than 100K lines long. Some symbolic
+The amalgamation source file is more than 200K lines long. Some symbolic
debuggers (most notably MSVC) are unable to deal with files longer than 64K
lines. To work around this, a separate Tcl script, tool/split-sqlite3c.tcl,
can be run on the amalgamation to break it up into a single small C file
-called **sqlite3-all.c** that does #include on about five other files
-named **sqlite3-1.c**, **sqlite3-2.c**, ..., **sqlite3-5.c**. In this way,
+called **sqlite3-all.c** that does #include on about seven other files
+named **sqlite3-1.c**, **sqlite3-2.c**, ..., **sqlite3-7.c**. In this way,
all of the source code is contained within a single translation unit so
that the compiler can do extra cross-procedure optimization, but no
individual source file exceeds 32K lines in length.
@@ -163,31 +308,39 @@ individual source file exceeds 32K lines in length.
## How It All Fits Together
SQLite is modular in design.
-See the [architectural description](http://www.sqlite.org/arch.html)
+See the [architectural description](https://www.sqlite.org/arch.html)
for details. Other documents that are useful in
-(helping to understand how SQLite works include the
-[file format](http://www.sqlite.org/fileformat2.html) description,
-the [virtual machine](http://www.sqlite.org/vdbe.html) that runs
+helping to understand how SQLite works include the
+[file format](https://www.sqlite.org/fileformat2.html) description,
+the [virtual machine](https://www.sqlite.org/opcode.html) that runs
prepared statements, the description of
-[how transactions work](http://www.sqlite.org/atomiccommit.html), and
-the [overview of the query planner](http://www.sqlite.org/optoverview.html).
+[how transactions work](https://www.sqlite.org/atomiccommit.html), and
+the [overview of the query planner](https://www.sqlite.org/optoverview.html).
-Unfortunately, years of effort have gone into optimizating SQLite, both
+Decades of effort have gone into optimizing SQLite, both
for small size and high performance. And optimizations tend to result in
-complex code. So there is a lot of complexity in the SQLite implementation.
+complex code. So there is a lot of complexity in the current SQLite
+implementation. It will not be the easiest library in the world to hack.
-Key files:
+### Key source code files
* **sqlite.h.in** - This file defines the public interface to the SQLite
library. Readers will need to be familiar with this interface before
- trying to understand how the library works internally.
+ trying to understand how the library works internally. This file is
+ really a template that is transformed into the "sqlite3.h" deliverable
+ using a script invoked by the makefile.
* **sqliteInt.h** - this header file defines many of the data objects
- used internally by SQLite.
+ used internally by SQLite. In addition to "sqliteInt.h", some
+ subsystems inside of sQLite have their own header files. These internal
+ interfaces are not for use by applications. They can and do change
+ from one release of SQLite to the next.
- * **parse.y** - This file describes the LALR(1) grammer that SQLite uses
+ * **parse.y** - This file describes the LALR(1) grammar that SQLite uses
to parse SQL statements, and the actions that are taken at each step
- in the parsing process.
+ in the parsing process. The file is processed by the
+ [Lemon Parser Generator](./doc/lemon.html) to produce the actual C code
+ used for parsing.
* **vdbe.c** - This file implements the virtual machine that runs
prepared statements. There are various helper files whose names
@@ -195,36 +348,85 @@ Key files:
which defines internal data objects. The rest of SQLite interacts
with the VDBE through an interface defined by vdbe.h.
- * **where.c** - This file analyzes the WHERE clause and generates
+ * **where.c** - This file (together with its helper files named
+ by "where*.c") analyzes the WHERE clause and generates
virtual machine code to run queries efficiently. This file is
sometimes called the "query optimizer". It has its own private
header file, whereInt.h, that defines data objects used internally.
* **btree.c** - This file contains the implementation of the B-Tree
- storage engine used by SQLite.
+ storage engine used by SQLite. The interface to the rest of the system
+ is defined by "btree.h". The "btreeInt.h" header defines objects
+ used internally by btree.c and not published to the rest of the system.
* **pager.c** - This file contains the "pager" implementation, the
- module that implements transactions.
+ module that implements transactions. The "pager.h" header file
+ defines the interface between pager.c and the rest of the system.
* **os_unix.c** and **os_win.c** - These two files implement the interface
between SQLite and the underlying operating system using the run-time
pluggable VFS interface.
- * **shell.c** - This file is not part of the core SQLite library. This
+ * **shell.c.in** - This file is not part of the core SQLite library. This
is the file that, when linked against sqlite3.a, generates the
- "sqlite3.exe" command-line shell.
+ "sqlite3.exe" command-line shell. The "shell.c.in" file is transformed
+ into "shell.c" as part of the build process.
* **tclsqlite.c** - This file implements the Tcl bindings for SQLite. It
is not part of the core SQLite library. But as most of the tests in this
repository are written in Tcl, the Tcl language bindings are important.
-There are many other source files. Each has a suscinct header comment that
+ * **test\*.c** - Files in the src/ folder that begin with "test" go into
+ building the "testfixture.exe" program. The testfixture.exe program is
+ an enhanced Tcl shell. The testfixture.exe program runs scripts in the
+ test/ folder to validate the core SQLite code. The testfixture program
+ (and some other test programs too) is built and run when you type
+ "make test".
+
+ * **VERSION**, **manifest**, and **manifest.uuid** - These files define
+ the current SQLite version number. The "VERSION" file is human generated,
+ but the "manifest" and "manifest.uuid" files are automatically generated
+ by the [Fossil version control system](https://fossil-scm.org/).
+
+There are many other source files. Each has a succinct header comment that
describes its purpose and role within the larger system.
+
+## Verifying Code Authenticity
+
+The `manifest` file at the root directory of the source tree
+contains either a SHA3-256 hash or a SHA1 hash
+for every source file in the repository.
+The name of the version of the entire source tree is just the
+SHA3-256 hash of the `manifest` file itself, possibly with the
+last line of that file omitted if the last line begins with
+"`# Remove this line`".
+The `manifest.uuid` file should contain the SHA3-256 hash of the
+`manifest` file. If all of the above hash comparisons are correct, then
+you can be confident that your source tree is authentic and unadulterated.
+Details on the format for the `manifest` files are available
+[on the Fossil website](https://fossil-scm.org/home/doc/trunk/www/fileformat.wiki#manifest).
+
+The process of checking source code authenticity is automated by the
+makefile:
+
+> make verify-source
+
+Or on windows:
+
+> nmake /f Makefile.msc verify-source
+
+Using the makefile to verify source integrity is good for detecting
+accidental changes to the source tree, but malicious changes could be
+hidden by also modifying the makefiles.
## Contacts
-The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/)
-with geographically distributed backup servers at
-[http://www2.sqlite.org/](http://www2.sqlite.org) and
-[http://www3.sqlite.org/](http://www3.sqlite.org).
+The main SQLite website is [https://sqlite.org/](https://sqlite.org/)
+with geographically distributed backups at
+[https://www2.sqlite.org/](https://www2.sqlite.org) and
+[https://www3.sqlite.org/](https://www3.sqlite.org).
+
+Contact the SQLite developers through the
+[SQLite Forum](https://sqlite.org/forum/). In an emergency, you
+can send private email to the lead developer at drh at sqlite dot org.
diff --git a/sqlite/VERSION b/sqlite/VERSION
index 49547fdc..7fd452d4 100644
--- a/sqlite/VERSION
+++ b/sqlite/VERSION
@@ -1 +1 @@
-3.14.1
+3.47.2
diff --git a/sqlite/art/icon-243x273.gif b/sqlite/art/icon-243x273.gif
new file mode 100644
index 00000000..e1cdfd0b
Binary files /dev/null and b/sqlite/art/icon-243x273.gif differ
diff --git a/sqlite/art/icon-80x90.gif b/sqlite/art/icon-80x90.gif
new file mode 100644
index 00000000..ebb23900
Binary files /dev/null and b/sqlite/art/icon-80x90.gif differ
diff --git a/sqlite/art/sqlite370.svg b/sqlite/art/sqlite370.svg
new file mode 100644
index 00000000..9a050b59
--- /dev/null
+++ b/sqlite/art/sqlite370.svg
@@ -0,0 +1,104 @@
+
+
+
+
diff --git a/sqlite/autoconf/Makefile.am b/sqlite/autoconf/Makefile.am
index e8211596..1eaa560f 100644
--- a/sqlite/autoconf/Makefile.am
+++ b/sqlite/autoconf/Makefile.am
@@ -1,6 +1,5 @@
-AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @SESSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE
-
+AM_CFLAGS = @BUILD_CFLAGS@
lib_LTLIBRARIES = libsqlite3.la
libsqlite3_la_SOURCES = sqlite3.c
libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8
@@ -10,11 +9,11 @@ sqlite3_SOURCES = shell.c sqlite3.h
EXTRA_sqlite3_SOURCES = sqlite3.c
sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
-sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS
+sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
include_HEADERS = sqlite3.h sqlite3ext.h
-EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs
+EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc sqlite3rc.h README.txt Replace.cs Makefile.fallback
pkgconfigdir = ${libdir}/pkgconfig
pkgconfig_DATA = sqlite3.pc
diff --git a/sqlite/autoconf/Makefile.fallback b/sqlite/autoconf/Makefile.fallback
new file mode 100644
index 00000000..9355b147
--- /dev/null
+++ b/sqlite/autoconf/Makefile.fallback
@@ -0,0 +1,19 @@
+#!/usr/bin/make
+#
+# If the configure script does not work, then this Makefile is available
+# as a backup. Manually configure the variables below.
+#
+# Note: This makefile works out-of-the-box on MacOS 10.2 (Jaguar)
+#
+CC = gcc
+CFLAGS = -O0 -I.
+LIBS = -lz
+COPTS += -D_BSD_SOURCE
+COPTS += -DSQLITE_ENABLE_LOCKING_STYLE=0
+COPTS += -DSQLITE_THREADSAFE=0
+COPTS += -DSQLITE_OMIT_LOAD_EXTENSION
+COPTS += -DSQLITE_WITHOUT_ZONEMALLOC
+COPTS += -DSQLITE_ENABLE_RTREE
+
+sqlite3: shell.c sqlite3.c
+ $(CC) $(CFLAGS) $(COPTS) -o sqlite3 shell.c sqlite3.c $(LIBS)
diff --git a/sqlite/autoconf/Makefile.msc b/sqlite/autoconf/Makefile.msc
index d3bbc6b4..efebc993 100644
--- a/sqlite/autoconf/Makefile.msc
+++ b/sqlite/autoconf/Makefile.msc
@@ -18,10 +18,24 @@
TOP = .
+# Optionally set EXTRA_SRC to a list of C files to append to
+# the generated sqlite3.c.
+#
+!IFNDEF EXTRA_SRC
+EXTRA_SRC =
+!ENDIF
+
# Set this non-0 to enable full warnings (-W4, etc) when compiling.
#
!IFNDEF USE_FULLWARN
-USE_FULLWARN = 0
+USE_FULLWARN = 1
+!ENDIF
+
+# Set this non-0 to enable treating warnings as errors (-WX, etc) when
+# compiling.
+#
+!IFNDEF USE_FATAL_WARN
+USE_FATAL_WARN = 0
!ENDIF
# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
@@ -31,6 +45,13 @@ USE_FULLWARN = 0
USE_RUNTIME_CHECKS = 0
!ENDIF
+# Set this non-0 to create a SQLite amalgamation file that excludes the
+# various built-in extensions.
+#
+!IFNDEF MINIMAL_AMALGAMATION
+MINIMAL_AMALGAMATION = 0
+!ENDIF
+
# Set this non-0 to use "stdcall" calling convention for the core library
# and shell executable.
#
@@ -38,6 +59,21 @@ USE_RUNTIME_CHECKS = 0
USE_STDCALL = 0
!ENDIF
+# Use the USE_SEH=0 option on the nmake command line to omit structured
+# exception handling (SEH) support. SEH is on by default.
+#
+!IFNDEF USE_SEH
+USE_SEH = 1
+!ENDIF
+
+# Use STATICALLY_LINK_TCL=1 to statically link against TCL
+#
+!IFNDEF STATICALLY_LINK_TCL
+STATICALLY_LINK_TCL = 0
+!ELSEIF $(STATICALLY_LINK_TCL)!=0
+CCOPTS = $(CCOPTS) -DSTATIC_BUILD
+!ENDIF
+
# Set this non-0 to have the shell executable link against the core dynamic
# link library.
#
@@ -59,7 +95,7 @@ API_ARMOR = 0
!IFNDEF NO_WARN
!IF $(USE_FULLWARN)!=0
NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4130 -wd4152 -wd4189 -wd4206
-NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4305 -wd4306 -wd4702 -wd4706
+NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
@@ -166,6 +202,12 @@ WIN32HEAP = 0
OSTRACE = 0
!ENDIF
+# enable address sanitizer using ASAN=1 on the command-line.
+#
+!IFNDEF ASAN
+ASAN = 0
+!ENDIF
+
# Set this to one of the following values to enable various debugging
# features. Each level includes the debugging options from the previous
# levels. Currently, the recognized values for DEBUG are:
@@ -182,6 +224,7 @@ OSTRACE = 0
DEBUG = 0
!ENDIF
+
# Enable use of available compiler optimizations? Normally, this should be
# non-zero. Setting this to zero, thus disabling all compiler optimizations,
# can be useful for testing.
@@ -196,6 +239,12 @@ OPTIMIZATIONS = 2
SESSION = 0
!ENDIF
+# Set this to non-0 to enable support for the rbu extension.
+#
+!IFNDEF RBU
+RBU = 0
+!ENDIF
+
# Set the source code file to be used by executables and libraries when
# they need the amalgamation.
#
@@ -255,15 +304,33 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
!ENDIF
!ENDIF
+
# These are the "standard" SQLite compilation options used when compiling for
# the Windows platform.
#
!IFNDEF OPT_FEATURE_FLAGS
+OPT_FEATURE_FLAGS = $(OPT_XTRA)
+!IF $(MINIMAL_AMALGAMATION)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
+!ENDIF
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
!ENDIF
+# Additional feature-options above and beyond what are normally used can be
+# be added using OPTIONS=.... on the command-line. These values are
+# appended to the OPT_FEATURE_FLAGS variable.
+#
+!IFDEF OPTIONS
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) $(OPTIONS)
+!ENDIF
+
# Should the session extension be enabled? If so, add compilation options
# to enable it.
#
@@ -272,6 +339,24 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
!ENDIF
+# Always enable math functions on Windows
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
+
+# Should the rbu extension be enabled? If so, add compilation options
+# to enable it.
+#
+!IF $(RBU)!=0
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
+!ENDIF
+
+# Should structured exception handling (SEH) be enabled for WAL mode in
+# the core library? It is on by default. Only omit it if the
+# USE_SEH=0 option is provided on the nmake command-line.
+#
+!IF $(USE_SEH)==0
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
+!ENDIF
+
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#
@@ -409,9 +494,9 @@ UCRTLIBPATH = $(UCRTLIBPATH:\\=\)
# will run on the platform that is doing the build.
#
!IF $(USE_FULLWARN)!=0
-BCC = $(NCC) -nologo -W4 $(CCOPTS) $(BCCOPTS)
+BCC = $(NCC) -nologo -W4 -Fd$*.pdb $(CCOPTS) $(BCCOPTS)
!ELSE
-BCC = $(NCC) -nologo -W3 $(CCOPTS) $(BCCOPTS)
+BCC = $(NCC) -nologo -W3 -Fd$*.pdb $(CCOPTS) $(BCCOPTS)
!ENDIF
# Check if assembly code listings should be generated for the source
@@ -444,6 +529,12 @@ TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS)
TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS)
!ENDIF
+# Check if warnings should be treated as errors when compiling.
+#
+!IF $(USE_FATAL_WARN)!=0
+TCC = $(TCC) -WX
+!ENDIF
+
TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -fp:precise
RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
@@ -456,12 +547,12 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
#
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
!IF "$(PLATFORM)"=="x86"
-CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
-SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
!ELSE
!IFNDEF PLATFORM
-CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
-SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
+SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
@@ -538,6 +629,7 @@ SHELL_CORE_DEP =
!ENDIF
!ENDIF
+
# This is the core library that the shell executable should link with.
#
!IFNDEF SHELL_CORE_LIB
@@ -622,6 +714,10 @@ RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
!IF $(DEBUG)>2
TCC = $(TCC) -DSQLITE_DEBUG=1
RCC = $(RCC) -DSQLITE_DEBUG=1
+!IF $(DYNAMIC_SHELL)==0
+TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
+RCC = $(RCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
+!ENDIF
!ENDIF
!IF $(DEBUG)>4 || $(OSTRACE)!=0
@@ -668,6 +764,13 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
!ENDIF
+# Address sanitizer if ASAN=1
+#
+!IF $(ASAN)>0
+TCC = $(TCC) /fsanitize=address
+!ENDIF
+
+
# Compiler options needed for programs that use the readline() library.
#
!IFNDEF READLINE_FLAGS
@@ -773,7 +876,7 @@ BCC = $(BCC) -Zi
# Command line prefixes for compiling code, compiling resources,
# linking, etc.
#
-LTCOMPILE = $(TCC) -Fo$@
+LTCOMPILE = $(TCC) -Fo$@ -Fd$*.pdb
LTRCOMPILE = $(RCC) -r
LTLIB = lib.exe
LTLINK = $(TCC) -Fe$@
@@ -781,7 +884,7 @@ LTLINK = $(TCC) -Fe$@
# If requested, link to the RPCRT4 library.
#
!IF $(USE_RPCRT4_LIB)!=0
-LTLINK = $(LTLINK) rpcrt4.lib
+LTLIBS = $(LTLIBS) rpcrt4.lib
!ENDIF
# If a platform was set, force the linker to target that.
@@ -791,6 +894,11 @@ LTLINK = $(LTLINK) rpcrt4.lib
!IFDEF PLATFORM
LTLINKOPTS = /NOLOGO /MACHINE:$(PLATFORM)
LTLIBOPTS = /NOLOGO /MACHINE:$(PLATFORM)
+!ELSEIF "$(VISUALSTUDIOVERSION)"=="12.0" || \
+ "$(VISUALSTUDIOVERSION)"=="14.0" || \
+ "$(VISUALSTUDIOVERSION)"=="15.0"
+LTLINKOPTS = /NOLOGO /MACHINE:x86
+LTLIBOPTS = /NOLOGO /MACHINE:x86
!ELSE
LTLINKOPTS = /NOLOGO
LTLIBOPTS = /NOLOGO
@@ -900,14 +1008,28 @@ LIBRESOBJS =
# when the shell is not being dynamically linked.
#
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
-SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1
!ENDIF
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
-all: dll shell
+core: dll shell
+
+# Targets that require the Tcl library.
+#
+tcl: $(ALL_TCL_TARGETS)
+
+# This Makefile target builds all of the standard binaries.
+#
+all: core tcl
# Dynamic link library section.
#
@@ -927,11 +1049,11 @@ Replace.exe:
sqlite3.def: Replace.exe $(LIBOBJ)
echo EXPORTS > sqlite3.def
dumpbin /all $(LIBOBJ) \
- | .\Replace.exe "^\s+/EXPORT:_?(sqlite3_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
+ | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser|rbu)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
| sort >> sqlite3.def
-$(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
- $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\shell.c $(SHELL_CORE_SRC) \
+$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
+ $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
@@ -946,7 +1068,7 @@ sqlite3.lo: $(SQLITE3C)
!IF $(USE_RC)!=0
_HASHCHAR=^#
!IF ![echo !IFNDEF VERSION > rcver.vc] && \
- ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| find "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
+ ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| "%SystemRoot%\System32\find.exe" "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
![echo !ENDIF >> rcver.vc]
!INCLUDE rcver.vc
!ENDIF
diff --git a/sqlite/autoconf/README.txt b/sqlite/autoconf/README.txt
index 6e62a4e1..b3d35107 100644
--- a/sqlite/autoconf/README.txt
+++ b/sqlite/autoconf/README.txt
@@ -9,8 +9,37 @@ This package contains:
* a Makefile.msc, sqlite3.rc, and Replace.cs for building with Microsoft
Visual C++ on Windows
-SUMMARY OF HOW TO BUILD
-=======================
+WHY USE THIS PACKAGE?
+=====================
+
+The canonical make system for SQLite requires TCL as part of the build
+process. Various TCL scripts are used to generate parts of the code and
+TCL is used to run tests. But some people would prefer to build SQLite
+using only generic tools and without having to install TCL. The purpose
+of this package is to provide that capability.
+
+This package contains a pre-build SQLite amalgamation file "sqlite3.c"
+(and its associated header file "sqlite3.h"). Because the amalgamation
+has been pre-built, no TCL is required.
+
+REASONS TO USE THE CANONICAL BUILD SYSTEM RATHER THAN THIS PACKAGE
+==================================================================
+
+ * the cononical build system allows you to run tests to verify that
+ the build worked
+ * the canonical build system supports more compile-time options
+ * the canonical build system works for any arbitrary check-in to
+ the SQLite source tree
+
+Step-by-step instructions on how to build using the canonical make
+system for SQLite can be found at:
+
+ https://sqlite.org/src/doc/trunk/doc/compile-for-unix.md
+ https://sqlite.org/src/doc/trunk/doc/compile-for-windows.md
+
+
+SUMMARY OF HOW TO BUILD USING THIS PACKAGE
+==========================================
Unix: ./configure; make
Windows: nmake /f Makefile.msc
@@ -53,48 +82,6 @@ Using Microsoft Visual C++ 2005 (or later) is recommended. Several Windows
platform variants may be built by adding additional macros to the NMAKE
command line.
-Building for WinRT 8.0
-----------------------
-
- FOR_WINRT=1
-
-Using Microsoft Visual C++ 2012 (or later) is required. When using the
-above, something like the following macro will need to be added to the
-NMAKE command line as well:
-
- "NSDKLIBPATH=%WindowsSdkDir%\..\8.0\lib\win8\um\x86"
-
-Building for WinRT 8.1
-----------------------
-
- FOR_WINRT=1
-
-Using Microsoft Visual C++ 2013 (or later) is required. When using the
-above, something like the following macro will need to be added to the
-NMAKE command line as well:
-
- "NSDKLIBPATH=%WindowsSdkDir%\..\8.1\lib\winv6.3\um\x86"
-
-Building for UWP 10.0
----------------------
-
- FOR_WINRT=1 FOR_UWP=1
-
-Using Microsoft Visual C++ 2015 (or later) is required. When using the
-above, something like the following macros will need to be added to the
-NMAKE command line as well:
-
- "NSDKLIBPATH=%WindowsSdkDir%\..\10\lib\10.0.10586.0\um\x86"
- "PSDKLIBPATH=%WindowsSdkDir%\..\10\lib\10.0.10586.0\um\x86"
- "NUCRTLIBPATH=%UniversalCRTSdkDir%\..\10\lib\10.0.10586.0\ucrt\x86"
-
-Building for the Windows 10 SDK
--------------------------------
-
- FOR_WIN10=1
-
-Using Microsoft Visual C++ 2015 (or later) is required. When using the
-above, no other macros should be needed on the NMAKE command line.
Other preprocessor defines
--------------------------
@@ -105,7 +92,7 @@ may be specified in this manner as some require the amalgamation to be built
with them enabled (see http://www.sqlite.org/compile.html). For example, the
following will work:
- "OPTS=-DSQLITE_ENABLE_STAT4=1 -DSQLITE_ENABLE_JSON1=1"
+ "OPTS=-DSQLITE_ENABLE_STAT4=1 -DSQLITE_OMIT_JSON=1"
However, the following will not compile unless the amalgamation was built
with it enabled:
diff --git a/sqlite/autoconf/configure.ac b/sqlite/autoconf/configure.ac
index b9a11aac..0c7a32db 100644
--- a/sqlite/autoconf/configure.ac
+++ b/sqlite/autoconf/configure.ac
@@ -12,6 +12,7 @@
AC_PREREQ(2.61)
AC_INIT(sqlite, --SQLITE-VERSION--, http://www.sqlite.org)
AC_CONFIG_SRCDIR([sqlite3.c])
+AC_CONFIG_AUX_DIR([.])
# Use automake.
AM_INIT_AUTOMAKE([foreign])
@@ -28,6 +29,7 @@ AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r])
AC_FUNC_STRERROR_R
AC_CONFIG_FILES([Makefile sqlite3.pc])
+BUILD_CFLAGS=
AC_SUBST(BUILD_CFLAGS)
#-------------------------------------------------------------------------
@@ -55,9 +57,9 @@ AS_IF([ test x"$enable_editline" != xno ],[
LIBS=""
AC_SEARCH_LIBS([readline],[edit],[
AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline)
- READLINE_LIBS=$LIBS
+ READLINE_LIBS="$LIBS -ltinfo"
enable_readline=no
- ])
+ ],[],[-ltinfo])
AS_UNSET(ac_cv_search_readline)
LIBS=$sLIBS
])
@@ -85,13 +87,13 @@ AC_SUBST(READLINE_LIBS)
AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
[--enable-threadsafe], [build a thread-safe library [default=yes]])],
[], [enable_threadsafe=yes])
-THREADSAFE_FLAGS=-DSQLITE_THREADSAFE=0
-if test x"$enable_threadsafe" != "xno"; then
- THREADSAFE_FLAGS="-D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
+if test x"$enable_threadsafe" == "xno"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_THREADSAFE=0"
+else
+ BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
AC_SEARCH_LIBS(pthread_create, pthread)
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
fi
-AC_SUBST(THREADSAFE_FLAGS)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
@@ -103,36 +105,87 @@ AC_ARG_ENABLE(dynamic-extensions, [AS_HELP_STRING(
if test x"$enable_dynamic_extensions" != "xno"; then
AC_SEARCH_LIBS(dlopen, dl)
else
- DYNAMIC_EXTENSION_FLAGS=-DSQLITE_OMIT_LOAD_EXTENSION=1
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
AC_MSG_CHECKING([for whether to support dynamic extensions])
AC_MSG_RESULT($enable_dynamic_extensions)
-AC_SUBST(DYNAMIC_EXTENSION_FLAGS)
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# --enable-math
+#
+AC_ARG_ENABLE(math, [AS_HELP_STRING(
+ [--enable-math], [SQL math functions [default=yes]])],
+ [], [enable_math=yes])
+AC_MSG_CHECKING([SQL math functions])
+if test x"$enable_math" = "xyes"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_MATH_FUNCTIONS"
+ AC_MSG_RESULT([enabled])
+ AC_SEARCH_LIBS(ceil, m)
+else
+ AC_MSG_RESULT([disabled])
+fi
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# --enable-fts4
+#
+AC_ARG_ENABLE(fts4, [AS_HELP_STRING(
+ [--enable-fts4], [include fts4 support [default=yes]])],
+ [], [enable_fts4=yes])
+AC_MSG_CHECKING([FTS4 extension])
+if test x"$enable_fts4" = "xyes"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS4"
+ AC_MSG_RESULT([enabled])
+else
+ AC_MSG_RESULT([disabled])
+fi
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# --enable-fts3
+#
+AC_ARG_ENABLE(fts3, [AS_HELP_STRING(
+ [--enable-fts3], [include fts3 support [default=no]])],
+ [], [])
+AC_MSG_CHECKING([FTS3 extension])
+if test x"$enable_fts3" = "xyes" -a x"$enable_fts4" = "xno"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS3"
+ AC_MSG_RESULT([enabled])
+else
+ AC_MSG_RESULT([disabled])
+fi
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# --enable-fts5
#
AC_ARG_ENABLE(fts5, [AS_HELP_STRING(
- [--enable-fts5], [include fts5 support [default=no]])],
- [], [enable_fts5=no])
+ [--enable-fts5], [include fts5 support [default=yes]])],
+ [], [enable_fts5=yes])
+AC_MSG_CHECKING([FTS5 extension])
if test x"$enable_fts5" = "xyes"; then
+ AC_MSG_RESULT([enabled])
AC_SEARCH_LIBS(log, m)
- FTS5_FLAGS=-DSQLITE_ENABLE_FTS5
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS5"
+else
+ AC_MSG_RESULT([disabled])
fi
-AC_SUBST(FTS5_FLAGS)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
-# --enable-json1
+# --enable-rtree
#
-AC_ARG_ENABLE(json1, [AS_HELP_STRING(
- [--enable-json1], [include json1 support [default=no]])],
- [], [enable_json1=no])
-if test x"$enable_json1" = "xyes"; then
- JSON1_FLAGS=-DSQLITE_ENABLE_JSON1
+AC_ARG_ENABLE(rtree, [AS_HELP_STRING(
+ [--enable-rtree], [include rtree support [default=yes]])],
+ [], [enable_rtree=yes])
+AC_MSG_CHECKING([RTREE extension])
+if test x"$enable_rtree" = "xyes"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_GEOPOLY"
+ AC_MSG_RESULT([enabled])
+else
+ AC_MSG_RESULT([disabled])
fi
-AC_SUBST(JSON1_FLAGS)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
@@ -140,11 +193,30 @@ AC_SUBST(JSON1_FLAGS)
#
AC_ARG_ENABLE(session, [AS_HELP_STRING(
[--enable-session], [enable the session extension [default=no]])],
- [], [enable_session=no])
+ [], [])
+AC_MSG_CHECKING([Session extension])
if test x"$enable_session" = "xyes"; then
- SESSION_FLAGS="-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
+ AC_MSG_RESULT([enabled])
+else
+ AC_MSG_RESULT([disabled])
+fi
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# --enable-debug
+#
+AC_ARG_ENABLE(debug, [AS_HELP_STRING(
+ [--enable-debug], [build with debugging features enabled [default=no]])],
+ [], [])
+AC_MSG_CHECKING([Build type])
+if test x"$enable_debug" = "xyes"; then
+ BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
+ CFLAGS="-g -O0"
+ AC_MSG_RESULT([debug])
+else
+ AC_MSG_RESULT([release])
fi
-AC_SUBST(SESSION_FLAGS)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
@@ -163,6 +235,12 @@ AC_SUBST(EXTRA_SHELL_OBJ)
#-----------------------------------------------------------------------
AC_CHECK_FUNCS(posix_fallocate)
+AC_CHECK_HEADERS(zlib.h,[
+ AC_SEARCH_LIBS(deflate,z,[BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_HAVE_ZLIB"])
+])
+
+AC_SEARCH_LIBS(system,,,[SHELL_CFLAGS="-DSQLITE_NOHAVE_SYSTEM"])
+AC_SUBST(SHELL_CFLAGS)
#-----------------------------------------------------------------------
# UPDATE: Maybe it's better if users just set CFLAGS before invoking
diff --git a/sqlite/autoconf/tea/Makefile.in b/sqlite/autoconf/tea/Makefile.in
index 3e481dad..cc98ab18 100644
--- a/sqlite/autoconf/tea/Makefile.in
+++ b/sqlite/autoconf/tea/Makefile.in
@@ -11,8 +11,6 @@
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-#
-# RCS: @(#) $Id: Makefile.in,v 1.59 2005/07/26 19:17:02 mdejong Exp $
#========================================================================
# Add additional lines to handle any additional AC_SUBST cases that
@@ -60,6 +58,8 @@ PKG_HEADERS = @PKG_HEADERS@
#========================================================================
PKG_LIB_FILE = @PKG_LIB_FILE@
+PKG_LIB_FILE8 = @PKG_LIB_FILE8@
+PKG_LIB_FILE9 = @PKG_LIB_FILE9@
PKG_STUB_LIB_FILE = @PKG_STUB_LIB_FILE@
lib_BINARIES = $(PKG_LIB_FILE)
@@ -73,10 +73,11 @@ exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
+includedir = @includedir@
datarootdir = @datarootdir@
+runstatedir = @runstatedir@
datadir = @datadir@
mandir = @mandir@
-includedir = @includedir@
DESTDIR =
@@ -85,24 +86,24 @@ pkgdatadir = $(datadir)/$(PKG_DIR)
pkglibdir = $(libdir)/$(PKG_DIR)
pkgincludedir = $(includedir)/$(PKG_DIR)
-top_builddir = .
+top_builddir = @abs_top_builddir@
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_OPTIONS =
+INSTALL = @INSTALL@ $(INSTALL_OPTIONS)
+INSTALL_DATA_DIR = @INSTALL_DATA_DIR@
INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_LIBRARY = @INSTALL_LIBRARY@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
CC = @CC@
CFLAGS_DEFAULT = @CFLAGS_DEFAULT@
CFLAGS_WARNING = @CFLAGS_WARNING@
-CLEANFILES = @CLEANFILES@
EXEEXT = @EXEEXT@
LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@
MAKE_LIB = @MAKE_LIB@
-MAKE_SHARED_LIB = @MAKE_SHARED_LIB@
-MAKE_STATIC_LIB = @MAKE_STATIC_LIB@
MAKE_STUB_LIB = @MAKE_STUB_LIB@
OBJEXT = @OBJEXT@
RANLIB = @RANLIB@
@@ -117,8 +118,6 @@ TCL_SRC_DIR = @TCL_SRC_DIR@
#TK_BIN_DIR = @TK_BIN_DIR@
#TK_SRC_DIR = @TK_SRC_DIR@
-# This is no longer necessary even for packages that use private Tcl headers
-#TCL_TOP_DIR_NATIVE = @TCL_TOP_DIR_NATIVE@
# Not used, but retained for reference of what libs Tcl required
#TCL_LIBS = @TCL_LIBS@
@@ -132,41 +131,52 @@ TCL_SRC_DIR = @TCL_SRC_DIR@
EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR)
#EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR):$(TK_BIN_DIR)
TCLLIBPATH = $(top_builddir)
-TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library` \
- @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \
+TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library`
+PKG_ENV = @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \
PATH="$(EXTRA_PATH):$(PATH)" \
TCLLIBPATH="$(TCLLIBPATH)"
-# TK_LIBRARY=`@CYGPATH@ $(TK_SRC_DIR)/library`
TCLSH_PROG = @TCLSH_PROG@
-TCLSH = $(TCLSH_ENV) $(TCLSH_PROG)
+TCLSH = $(TCLSH_ENV) $(PKG_ENV) $(TCLSH_PROG)
+#WISH_ENV = TK_LIBRARY=`@CYGPATH@ $(TK_SRC_DIR)/library`
#WISH_PROG = @WISH_PROG@
-#WISH = $(TCLSH_ENV) $(WISH_PROG)
-
+#WISH = $(TCLSH_ENV) $(WISH_ENV) $(PKG_ENV) $(WISH_PROG)
SHARED_BUILD = @SHARED_BUILD@
-INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ -I$(srcdir)/..
+INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ -I. -I$(srcdir)/..
#INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ @TK_INCLUDES@ @TK_XINCLUDES@
PKG_CFLAGS = @PKG_CFLAGS@
# TCL_DEFS is not strictly need here, but if you remove it, then you
-# must make sure that configure.in checks for the necessary components
+# must make sure that configure.ac checks for the necessary components
# that your library may use. TCL_DEFS can actually be a problem if
# you do not compile with a similar machine setup as the Tcl core was
# compiled with.
#DEFS = $(TCL_DEFS) @DEFS@ $(PKG_CFLAGS)
DEFS = @DEFS@ $(PKG_CFLAGS)
+# Move pkgIndex.tcl to 'BINARIES' var if it is generated in the Makefile
CONFIG_CLEAN_FILES = Makefile pkgIndex.tcl
+CLEANFILES = @CLEANFILES@
CPPFLAGS = @CPPFLAGS@
LIBS = @PKG_LIBS@ @LIBS@
AR = @AR@
CFLAGS = @CFLAGS@
-COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LDFLAGS = @LDFLAGS@
+LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) \
+ $(CFLAGS_DEFAULT) $(CFLAGS_WARNING) $(SHLIB_CFLAGS) $(CFLAGS)
+
+GDB = gdb
+VALGRIND = valgrind
+VALGRINDARGS = --tool=memcheck --num-callers=8 --leak-resolution=high \
+ --leak-check=yes --show-reachable=yes -v
+
+.SUFFIXES: .c .$(OBJEXT)
#========================================================================
# Start of user-definable TARGETS section
@@ -174,7 +184,7 @@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(C
#========================================================================
# TEA TARGETS. Please note that the "libraries:" target refers to platform
-# independent files, and the "binaries:" target inclues executable programs and
+# independent files, and the "binaries:" target includes executable programs and
# platform-dependent libraries. Modify these targets so that they install
# the various pieces of your package. The make and install rules
# for the BINARIES that you specified above have already been done.
@@ -193,7 +203,6 @@ binaries: $(BINARIES)
libraries:
-
#========================================================================
# Your doc target should differentiate from doc builds (by the developer)
# and doc installs (see install-doc), which just install the docs on the
@@ -216,11 +225,11 @@ install-binaries: binaries install-lib-binaries install-bin-binaries
#========================================================================
install-libraries: libraries
- @mkdir -p $(DESTDIR)$(includedir)
+ @$(INSTALL_DATA_DIR) "$(DESTDIR)$(includedir)"
@echo "Installing header files in $(DESTDIR)$(includedir)"
@list='$(PKG_HEADERS)'; for i in $$list; do \
echo "Installing $(srcdir)/$$i" ; \
- $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir) ; \
+ $(INSTALL_DATA) $(srcdir)/$$i "$(DESTDIR)$(includedir)" ; \
done;
#========================================================================
@@ -229,12 +238,11 @@ install-libraries: libraries
#========================================================================
install-doc: doc
- @mkdir -p $(DESTDIR)$(mandir)/mann
+ @$(INSTALL_DATA_DIR) "$(DESTDIR)$(mandir)/mann"
@echo "Installing documentation in $(DESTDIR)$(mandir)"
@list='$(srcdir)/doc/*.n'; for i in $$list; do \
echo "Installing $$i"; \
- rm -f $(DESTDIR)$(mandir)/mann/`basename $$i`; \
- $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \
+ $(INSTALL_DATA) $$i "$(DESTDIR)$(mandir)/mann" ; \
done
test: binaries libraries
@@ -244,7 +252,21 @@ shell: binaries libraries
@$(TCLSH) $(SCRIPT)
gdb:
- $(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT)
+ $(TCLSH_ENV) $(PKG_ENV) $(GDB) $(TCLSH_PROG) $(SCRIPT)
+
+gdb-test: binaries libraries
+ $(TCLSH_ENV) $(PKG_ENV) $(GDB) \
+ --args $(TCLSH_PROG) `@CYGPATH@ $(srcdir)/tests/all.tcl` \
+ $(TESTFLAGS) -singleproc 1 \
+ -load "package ifneeded $(PACKAGE_NAME) $(PACKAGE_VERSION) \
+ [list load `@CYGPATH@ $(PKG_LIB_FILE)` [string totitle $(PACKAGE_NAME)]]"
+
+valgrind: binaries libraries
+ $(TCLSH_ENV) $(PKG_ENV) $(VALGRIND) $(VALGRINDARGS) $(TCLSH_PROG) \
+ `@CYGPATH@ $(srcdir)/tests/all.tcl` $(TESTFLAGS)
+
+valgrindshell: binaries libraries
+ $(TCLSH_ENV) $(PKG_ENV) $(VALGRIND) $(VALGRINDARGS) $(TCLSH_PROG) $(SCRIPT)
depend:
@@ -283,49 +305,57 @@ $(PKG_STUB_LIB_FILE): $(PKG_STUB_OBJECTS)
# As necessary, add $(srcdir):$(srcdir)/compat:....
#========================================================================
-VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win
+VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win:$(srcdir)/macosx
.c.@OBJEXT@:
$(COMPILE) -c `@CYGPATH@ $<` -o $@
+
#========================================================================
# Distribution creation
# You may need to tweak this target to make it work correctly.
#========================================================================
#COMPRESS = tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar
-COMPRESS = gtar zcvf $(PKG_DIR).tar.gz $(PKG_DIR)
+COMPRESS = tar zcvf $(PKG_DIR).tar.gz $(PKG_DIR)
DIST_ROOT = /tmp/dist
DIST_DIR = $(DIST_ROOT)/$(PKG_DIR)
+DIST_INSTALL_DATA = CPPROG='cp -p' $(INSTALL) -m 644
+DIST_INSTALL_SCRIPT = CPPROG='cp -p' $(INSTALL) -m 755
+
dist-clean:
rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.*
-dist: dist-clean
- mkdir -p $(DIST_DIR)
- cp -p $(srcdir)/README* $(srcdir)/license* \
- $(srcdir)/aclocal.m4 $(srcdir)/configure $(srcdir)/*.in \
- $(DIST_DIR)/
- chmod 664 $(DIST_DIR)/Makefile.in $(DIST_DIR)/aclocal.m4
- chmod 775 $(DIST_DIR)/configure $(DIST_DIR)/configure.in
-
- for i in $(srcdir)/*.[ch]; do \
- if [ -f $$i ]; then \
- cp -p $$i $(DIST_DIR)/ ; \
- fi; \
- done;
-
- mkdir $(DIST_DIR)/tclconfig
- cp $(srcdir)/tclconfig/install-sh $(srcdir)/tclconfig/tcl.m4 \
- $(DIST_DIR)/tclconfig/
- chmod 664 $(DIST_DIR)/tclconfig/tcl.m4
- chmod +x $(DIST_DIR)/tclconfig/install-sh
-
- list='demos doc generic library mac tests unix win'; \
+dist: dist-clean $(srcdir)/manifest.uuid
+ $(INSTALL_DATA_DIR) $(DIST_DIR)
+
+ # TEA files
+ $(DIST_INSTALL_DATA) $(srcdir)/Makefile.in \
+ $(srcdir)/aclocal.m4 $(srcdir)/configure.ac \
+ $(DIST_DIR)/
+ $(DIST_INSTALL_SCRIPT) $(srcdir)/configure $(DIST_DIR)/
+
+ $(INSTALL_DATA_DIR) $(DIST_DIR)/tclconfig
+ $(DIST_INSTALL_DATA) $(srcdir)/tclconfig/README.txt \
+ $(srcdir)/manifest.uuid \
+ $(srcdir)/tclconfig/tcl.m4 $(srcdir)/tclconfig/install-sh \
+ $(DIST_DIR)/tclconfig/
+
+ # Extension files
+ $(DIST_INSTALL_DATA) \
+ $(srcdir)/ChangeLog \
+ $(srcdir)/README.sha \
+ $(srcdir)/license.terms \
+ $(srcdir)/README \
+ $(srcdir)/pkgIndex.tcl.in \
+ $(DIST_DIR)/
+
+ list='demos doc generic library macosx tests unix win'; \
for p in $$list; do \
if test -d $(srcdir)/$$p ; then \
- mkdir $(DIST_DIR)/$$p; \
- cp -p $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \
+ $(INSTALL_DATA_DIR) $(DIST_DIR)/$$p; \
+ $(DIST_INSTALL_DATA) $(srcdir)/$$p/* $(DIST_DIR)/$$p/; \
fi; \
done
@@ -337,10 +367,10 @@ dist: dist-clean
#========================================================================
# Don't modify the file to clean here. Instead, set the "CLEANFILES"
-# variable in configure.in
+# variable in configure.ac
#========================================================================
-clean:
+clean:
-test -z "$(BINARIES)" || rm -f $(BINARIES)
-rm -f *.$(OBJEXT) core *.core
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -348,7 +378,7 @@ clean:
distclean: clean
-rm -f *.tab.c
-rm -f $(CONFIG_CLEAN_FILES)
- -rm -f config.h config.cache config.log config.status
+ -rm -f config.cache config.log config.status
#========================================================================
# Install binary object libraries. On Windows this includes both .dll and
@@ -362,25 +392,17 @@ distclean: clean
#========================================================================
install-lib-binaries: binaries
- @mkdir -p $(DESTDIR)$(pkglibdir)
+ @$(INSTALL_DATA_DIR) "$(DESTDIR)$(pkglibdir)"
@list='$(lib_BINARIES)'; for p in $$list; do \
if test -f $$p; then \
- echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p"; \
- $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p; \
- stub=`echo $$p|sed -e "s/.*\(stub\).*/\1/"`; \
- if test "x$$stub" = "xstub"; then \
- echo " $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p"; \
- $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p; \
- else \
- echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \
- $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \
- fi; \
+ echo " $(INSTALL_LIBRARY) $$p $(DESTDIR)$(pkglibdir)/$$p"; \
+ $(INSTALL_LIBRARY) $$p "$(DESTDIR)$(pkglibdir)/$$p"; \
ext=`echo $$p|sed -e "s/.*\.//"`; \
if test "x$$ext" = "xdll"; then \
lib=`basename $$p|sed -e 's/.[^.]*$$//'`.lib; \
if test -f $$lib; then \
echo " $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib"; \
- $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib; \
+ $(INSTALL_DATA) $$lib "$(DESTDIR)$(pkglibdir)/$$lib"; \
fi; \
fi; \
fi; \
@@ -389,12 +411,12 @@ install-lib-binaries: binaries
if test -f $(srcdir)/$$p; then \
destp=`basename $$p`; \
echo " Install $$destp $(DESTDIR)$(pkglibdir)/$$destp"; \
- $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkglibdir)/$$destp; \
+ $(INSTALL_DATA) $(srcdir)/$$p "$(DESTDIR)$(pkglibdir)/$$destp"; \
fi; \
done
@if test "x$(SHARED_BUILD)" = "x1"; then \
echo " Install pkgIndex.tcl $(DESTDIR)$(pkglibdir)"; \
- $(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(pkglibdir); \
+ $(INSTALL_DATA) pkgIndex.tcl "$(DESTDIR)$(pkglibdir)"; \
fi
#========================================================================
@@ -407,11 +429,11 @@ install-lib-binaries: binaries
#========================================================================
install-bin-binaries: binaries
- @mkdir -p $(DESTDIR)$(bindir)
+ @$(INSTALL_DATA_DIR) "$(DESTDIR)$(bindir)"
@list='$(bin_BINARIES)'; for p in $$list; do \
if test -f $$p; then \
echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p"; \
- $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p; \
+ $(INSTALL_PROGRAM) $$p "$(DESTDIR)$(bindir)/$$p"; \
fi; \
done
@@ -423,17 +445,18 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
uninstall-binaries:
list='$(lib_BINARIES)'; for p in $$list; do \
- rm -f $(DESTDIR)$(pkglibdir)/$$p; \
+ rm -f "$(DESTDIR)$(pkglibdir)/$$p"; \
done
list='$(PKG_TCL_SOURCES)'; for p in $$list; do \
p=`basename $$p`; \
- rm -f $(DESTDIR)$(pkglibdir)/$$p; \
+ rm -f "$(DESTDIR)$(pkglibdir)/$$p"; \
done
list='$(bin_BINARIES)'; for p in $$list; do \
- rm -f $(DESTDIR)$(bindir)/$$p; \
+ rm -f "$(DESTDIR)$(bindir)/$$p"; \
done
.PHONY: all binaries clean depend distclean doc install libraries test
+.PHONY: gdb gdb-test valgrind valgrindshell
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/sqlite/autoconf/tea/README b/sqlite/autoconf/tea/README
deleted file mode 100644
index 99dc8b8f..00000000
--- a/sqlite/autoconf/tea/README
+++ /dev/null
@@ -1,36 +0,0 @@
-This is the SQLite extension for Tcl using the Tcl Extension
-Architecture (TEA). For additional information on SQLite see
-
- http://www.sqlite.org/
-
-
-UNIX BUILD
-==========
-
-Building under most UNIX systems is easy, just run the configure script
-and then run make. For more information about the build process, see
-the tcl/unix/README file in the Tcl src dist. The following minimal
-example will install the extension in the /opt/tcl directory.
-
- $ cd sqlite-*-tea
- $ ./configure --prefix=/opt/tcl
- $ make
- $ make install
-
-WINDOWS BUILD
-=============
-
-The recommended method to build extensions under windows is to use the
-Msys + Mingw build process. This provides a Unix-style build while
-generating native Windows binaries. Using the Msys + Mingw build tools
-means that you can use the same configure script as per the Unix build
-to create a Makefile. See the tcl/win/README file for the URL of
-the Msys + Mingw download.
-
-If you have VC++ then you may wish to use the files in the win
-subdirectory and build the extension using just VC++. These files have
-been designed to be as generic as possible but will require some
-additional maintenance by the project developer to synchronise with
-the TEA configure.in and Makefile.in files. Instructions for using the
-VC++ makefile are written in the first part of the Makefile.vc
-file.
diff --git a/sqlite/autoconf/tea/README.txt b/sqlite/autoconf/tea/README.txt
new file mode 100644
index 00000000..b50d4f29
--- /dev/null
+++ b/sqlite/autoconf/tea/README.txt
@@ -0,0 +1,78 @@
+This is the SQLite extension for Tcl using the Tcl Extension
+Architecture (TEA).
+
+----------------------- A BETTER WAY ---------------------------
+
+A better way to build the TCL extension for SQLite is to use the
+canonical source code tarball. For Unix:
+
+ ./configure --with-tclsh=$(TCLSH)
+ make tclextension-install
+
+For Windows:
+
+ nmake /f Makefile.msc tclextension-install TCLSH_CMD=$(TCLSH)
+
+In both of the above, replace $(TCLSH) with the full pathname of
+of the tclsh that you want the SQLite extension to work with. See
+step-by-step instructions at the links below for more information:
+
+ https://sqlite.org/src/doc/trunk/doc/compile-for-unix.md
+ https://sqlite.org/src/doc/trunk/doc/compile-for-windows.md
+
+The whole point of the amalgamation-autoconf tarball (in which this
+README.txt file is embedded) is to provide a means of compiling
+SQLite that does not require first installing TCL and/or "tclsh".
+The canonical Makefile in the SQLite source tree provides more
+capabilities (such as the the ability to run test cases to ensure
+that the build worked) and is better maintained. The only
+downside of the canonical Makfile is that it requires a TCL
+installation. But if you are wanting to build the TCL extension for
+SQLite, then presumably you already have a TCL installation. So why
+not just use the more-capable and better-maintained canoncal Makefile?
+
+This TEA builder is derived from code found at
+
+ http://core.tcl-lang.org/tclconfig
+ http://core.tcl-lang.org/sampleextension
+
+The SQLite developers do not understand how it works. It seems to
+work for us. It might also work for you. But we cannot promise that.
+
+If you want to use this TEA builder and it works for you, that's fine.
+But if you have trouble, the first thing you should do is go back
+to using the canonical Makefile in the SQLite source tree.
+
+------------------------------------------------------------------
+
+
+UNIX BUILD
+==========
+
+Building under most UNIX systems is easy, just run the configure script
+and then run make. For more information about the build process, see
+the tcl/unix/README file in the Tcl src dist. The following minimal
+example will install the extension in the /opt/tcl directory.
+
+ $ cd sqlite-*-tea
+ $ ./configure --prefix=/opt/tcl
+ $ make
+ $ make install
+
+WINDOWS BUILD
+=============
+
+The recommended method to build extensions under windows is to use the
+Msys + Mingw build process. This provides a Unix-style build while
+generating native Windows binaries. Using the Msys + Mingw build tools
+means that you can use the same configure script as per the Unix build
+to create a Makefile. See the tcl/win/README file for the URL of
+the Msys + Mingw download.
+
+If you have VC++ then you may wish to use the files in the win
+subdirectory and build the extension using just VC++. These files have
+been designed to be as generic as possible but will require some
+additional maintenance by the project developer to synchronise with
+the TEA configure.in and Makefile.in files. Instructions for using the
+VC++ makefile are written in the first part of the Makefile.vc
+file.
diff --git a/sqlite/autoconf/tea/configure.ac b/sqlite/autoconf/tea/configure.ac
index 7fca05b2..590f4f0b 100644
--- a/sqlite/autoconf/tea/configure.ac
+++ b/sqlite/autoconf/tea/configure.ac
@@ -2,11 +2,9 @@
dnl This file is an input file used by the GNU "autoconf" program to
dnl generate the file "configure", which is run during Tcl installation
dnl to configure the system for the local environment.
-#
-# RCS: @(#) $Id: configure.in,v 1.43 2005/07/26 19:17:05 mdejong Exp $
#-----------------------------------------------------------------------
-# Sample configure.in for Tcl Extensions. The only places you should
+# Sample configure.ac for Tcl Extensions. The only places you should
# need to modify this file are marked by the string __CHANGE__
#-----------------------------------------------------------------------
@@ -17,9 +15,11 @@ dnl to configure the system for the local environment.
# This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION
# set as provided. These will also be added as -D defs in your Makefile
# so you can encode the package version directly into the source files.
+# This will also define a special symbol for Windows (BUILD_
+# so that we create the export library with the dll.
#-----------------------------------------------------------------------
-AC_INIT([sqlite], [3.7.4])
+AC_INIT([sqlite],[3.47.2])
#--------------------------------------------------------------------
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
@@ -27,7 +27,7 @@ AC_INIT([sqlite], [3.7.4])
# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
#--------------------------------------------------------------------
-TEA_INIT([3.9])
+TEA_INIT()
AC_CONFIG_AUX_DIR(tclconfig)
@@ -55,8 +55,8 @@ TEA_PREFIX
#-----------------------------------------------------------------------
# Standard compiler checks.
# This sets up CC by using the CC env var, or looks for gcc otherwise.
-# This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create
-# the basic setup necessary to compile executables.
+# This also calls AC_PROG_CC and a few others to create the basic setup
+# necessary to compile executables.
#-----------------------------------------------------------------------
TEA_SETUP_COMPILER
@@ -73,11 +73,19 @@ TEA_SETUP_COMPILER
TEA_ADD_SOURCES([tclsqlite3.c])
TEA_ADD_HEADERS([])
-TEA_ADD_INCLUDES([-I\"`\${CYGPATH} \${srcdir}/generic`\"])
+TEA_ADD_INCLUDES([])
TEA_ADD_LIBS([])
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS4=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS5=1])
TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1])
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_GEOPOLY=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_MATH_FUNCTIONS=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DESERIALIZE=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DBPAGE_VTAB=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_BYTECODE_VTAB=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DBSTAT_VTAB=1])
TEA_ADD_STUB_SOURCES([])
TEA_ADD_TCL_SOURCES([])
@@ -89,7 +97,7 @@ TEA_ADD_TCL_SOURCES([])
# Patchs from rmax.
#--------------------------------------------------------------------
AC_ARG_WITH([system-sqlite],
- [AC_HELP_STRING([--with-system-sqlite],
+ [AS_HELP_STRING([--with-system-sqlite],
[use a system-supplied libsqlite3 instead of the bundled one])],
[], [with_system_sqlite=no])
if test x$with_system_sqlite != xno; then
@@ -99,6 +107,31 @@ if test x$with_system_sqlite != xno; then
LIBS="$LIBS -lsqlite3"])])
fi
+#--------------------------------------------------------------------
+# __CHANGE__
+#
+# You can add more files to clean if your extension creates any extra
+# files by extending CLEANFILES.
+# Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure
+# and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var.
+#
+# A few miscellaneous platform-specific items:
+# TEA_ADD_* any platform specific compiler/build info here.
+#--------------------------------------------------------------------
+
+#CLEANFILES="$CLEANFILES pkgIndex.tcl"
+if test "${TEA_PLATFORM}" = "windows" ; then
+ # Ensure no empty if clauses
+ :
+ #TEA_ADD_SOURCES([win/winFile.c])
+ #TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"])
+else
+ # Ensure no empty else clauses
+ :
+ #TEA_ADD_SOURCES([unix/unixFile.c])
+ #TEA_ADD_LIBS([-lsuperfly])
+fi
+
#--------------------------------------------------------------------
# __CHANGE__
# Choose which headers you need. Extension authors should try very
@@ -151,28 +184,6 @@ TEA_CONFIG_CFLAGS
TEA_ENABLE_SYMBOLS
-#--------------------------------------------------------------------
-# Everyone should be linking against the Tcl stub library. If you
-# can't for some reason, remove this definition. If you aren't using
-# stubs, you also need to modify the SHLIB_LD_LIBS setting below to
-# link against the non-stubbed Tcl library. Add Tk too if necessary.
-#--------------------------------------------------------------------
-
-AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs])
-#AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs])
-
-
-#--------------------------------------------------------------------
-# Redefine fdatasync as fsync on systems that lack fdatasync
-#--------------------------------------------------------------------
-#
-#AC_CHECK_FUNC(fdatasync, , AC_DEFINE(fdatasync, fsync))
-# Check for library functions that SQLite can optionally use.
-AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r])
-
-AC_FUNC_STRERROR_R
-
-
#--------------------------------------------------------------------
# This macro generates a line to use when building a library. It
# depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS,
@@ -193,9 +204,24 @@ TEA_PROG_TCLSH
#TEA_PROG_WISH
#--------------------------------------------------------------------
-# Finally, substitute all of the various values into the Makefile.
-# You may alternatively have a special pkgIndex.tcl.in or other files
-# which require substituting th AC variables in. Include these here.
+# Setup a *Config.sh.in configuration file.
+#--------------------------------------------------------------------
+
+#TEA_EXPORT_CONFIG([sample])
+#AC_SUBST(SAMPLE_VAR)
+
+#--------------------------------------------------------------------
+# Specify files to substitute AC variables in. You may alternatively
+# have a special pkgIndex.tcl.in or other files which require
+# substituting the AC variables in. Include these here.
+#--------------------------------------------------------------------
+
+AC_CONFIG_FILES([Makefile pkgIndex.tcl])
+#AC_CONFIG_FILES([sampleConfig.sh])
+
+#--------------------------------------------------------------------
+# Finally, substitute all of the various values into the files
+# specified with AC_CONFIG_FILES.
#--------------------------------------------------------------------
-AC_OUTPUT([Makefile pkgIndex.tcl])
+AC_OUTPUT
diff --git a/sqlite/autoconf/tea/pkgIndex.tcl.in b/sqlite/autoconf/tea/pkgIndex.tcl.in
index bc585f73..666812de 100644
--- a/sqlite/autoconf/tea/pkgIndex.tcl.in
+++ b/sqlite/autoconf/tea/pkgIndex.tcl.in
@@ -1,7 +1,10 @@
+# -*- tcl -*-
+# Tcl package index file, version 1.1
#
-# Tcl package index file
-#
-# Note sqlite*3* init specifically
-#
-package ifneeded sqlite3 @PACKAGE_VERSION@ \
- [list load [file join $dir @PKG_LIB_FILE@] Sqlite3]
+if {[package vsatisfies [package provide Tcl] 9.0-]} {
+ package ifneeded sqlite3 @PACKAGE_VERSION@ \
+ [list load [file join $dir @PKG_LIB_FILE9@] Sqlite3]
+} else {
+ package ifneeded sqlite3 @PACKAGE_VERSION@ \
+ [list load [file join $dir @PKG_LIB_FILE8@] Sqlite3]
+}
diff --git a/sqlite/autoconf/tea/tclconfig/install-sh b/sqlite/autoconf/tea/tclconfig/install-sh
index 7c34c3f9..ec298b53 100644
--- a/sqlite/autoconf/tea/tclconfig/install-sh
+++ b/sqlite/autoconf/tea/tclconfig/install-sh
@@ -1,7 +1,7 @@
#!/bin/sh
# install - install a program, script, or datafile
-scriptversion=2011-04-20.01; # UTC
+scriptversion=2020-11-14.01; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
@@ -35,25 +35,21 @@ scriptversion=2011-04-20.01; # UTC
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
+# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
+tab=' '
nl='
'
-IFS=" "" $nl"
+IFS=" $tab$nl"
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
-# Don't use :- since 4.3BSD and earlier shells don't like it.
doit=${DOITPROG-}
-if test -z "$doit"; then
- doit_exec=exec
-else
- doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
@@ -68,22 +64,16 @@ mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
-posix_glob='?'
-initialize_posix_glob='
- test "$posix_glob" != "?" || {
- if (set -f) 2>/dev/null; then
- posix_glob=
- else
- posix_glob=:
- fi
- }
-'
-
posix_mkdir=
# Desired mode of installed file.
mode=0755
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
@@ -97,7 +87,7 @@ dir_arg=
dst_arg=
copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -114,19 +104,28 @@ Options:
--version display version info and exit.
-c (ignored)
- -C install only if different (preserve the last data modification time)
+ -C install only if different (preserve data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
+ -p pass -p to $cpprog.
-s $stripprog installed files.
- -S $stripprog installed files.
+ -S SUFFIX attempt to back up existing files, with suffix SUFFIX.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
"
while test $# -ne 0; do
@@ -138,45 +137,62 @@ while test $# -ne 0; do
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
- shift;;
+ shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
-o) chowncmd="$chownprog $2"
- shift;;
+ shift;;
+
+ -p) cpprog="$cpprog -p";;
-s) stripcmd=$stripprog;;
- -S) stripcmd="$stripprog $2"
- shift;;
+ -S) backupsuffix="$2"
+ shift;;
- -t) dst_arg=$2
- shift;;
+ -t)
+ is_target_a_directory=always
+ dst_arg=$2
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
- -T) no_target_directory=true;;
+ -T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
- --) shift
- break;;
+ --) shift
+ break;;
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
*) break;;
esac
shift
done
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
@@ -190,6 +206,10 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
fi
shift # arg
dst_arg=$arg
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
done
fi
@@ -198,11 +218,20 @@ if test $# -eq 0; then
echo "$0: no input file specified." >&2
exit 1
fi
- # It's OK to call `install-sh -d' without argument.
+ # It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
+if test -z "$dir_arg"; then
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+fi
+
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
@@ -219,16 +248,16 @@ if test -z "$dir_arg"; then
*[0-7])
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw='% 200'
+ u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw=,u+rw
+ u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
@@ -236,9 +265,9 @@ fi
for src
do
- # Protect names starting with `-'.
+ # Protect names problematic for 'test' and other utilities.
case $src in
- -*) src=./$src;;
+ -* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
@@ -246,6 +275,10 @@ do
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
+ # Don't chown directories that already exist.
+ if test $dstdir_status = 0; then
+ chowncmd=""
+ fi
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
@@ -260,185 +293,150 @@ do
echo "$0: no destination specified." >&2
exit 1
fi
-
dst=$dst_arg
- # Protect names starting with `-'.
- case $dst in
- -*) dst=./$dst;;
- esac
- # If destination is a directory, append the input filename; won't work
- # if double slashes aren't ignored.
+ # If destination is a directory, append the input filename.
if test -d "$dst"; then
- if test -n "$no_target_directory"; then
- echo "$0: $dst_arg: Is a directory" >&2
- exit 1
+ if test "$is_target_a_directory" = never; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
fi
dstdir=$dst
- dst=$dstdir/`basename "$src"`
+ dstbase=`basename "$src"`
+ case $dst in
+ */) dst=$dst$dstbase;;
+ *) dst=$dst/$dstbase;;
+ esac
dstdir_status=0
else
- # Prefer dirname, but fall back on a substitute if dirname fails.
- dstdir=`
- (dirname "$dst") 2>/dev/null ||
- expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$dst" : 'X\(//\)[^/]' \| \
- X"$dst" : 'X\(//\)$' \| \
- X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
- echo X"$dst" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'
- `
-
+ dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
+ case $dstdir in
+ */) dstdirslash=$dstdir;;
+ *) dstdirslash=$dstdir/;;
+ esac
+
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
- # Create intermediate dirs using mode 755 as modified by the umask.
- # This is like FreeBSD 'install' as of 1997-10-28.
- umask=`umask`
- case $stripcmd.$umask in
- # Optimize common cases.
- *[2367][2367]) mkdir_umask=$umask;;
- .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
- *[0-7])
- mkdir_umask=`expr $umask + 22 \
- - $umask % 100 % 40 + $umask % 20 \
- - $umask % 10 % 4 + $umask % 2
- `;;
- *) mkdir_umask=$umask,go-w;;
- esac
-
- # With -d, create the new directory with the user-specified mode.
- # Otherwise, rely on $mkdir_umask.
- if test -n "$dir_arg"; then
- mkdir_mode=-m$mode
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ # The $RANDOM variable is not portable (e.g., dash). Use it
+ # here however when possible just to lower collision chance.
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+ trap '
+ ret=$?
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+ exit $ret
+ ' 0
+
+ # Because "mkdir -p" follows existing symlinks and we likely work
+ # directly in world-writeable /tmp, make sure that the '$tmpdir'
+ # directory is successfully created first before we actually test
+ # 'mkdir -p'.
+ if (umask $mkdir_umask &&
+ $mkdirprog $mkdir_mode "$tmpdir" &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ test_tmpdir="$tmpdir/a"
+ ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
- mkdir_mode=
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
-
- posix_mkdir=false
- case $umask in
- *[123567][0-7][0-7])
- # POSIX mkdir -p sets u+wx bits regardless of umask, which
- # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
- ;;
- *)
- tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
- trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
- if (umask $mkdir_umask &&
- exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
- then
- if test -z "$dir_arg" || {
- # Check for POSIX incompatibilities with -m.
- # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
- # other-writeable bit of parent directory when it shouldn't.
- # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
- ls_ld_tmpdir=`ls -ld "$tmpdir"`
- case $ls_ld_tmpdir in
- d????-?r-*) different_mode=700;;
- d????-?--*) different_mode=755;;
- *) false;;
- esac &&
- $mkdirprog -m$different_mode -p -- "$tmpdir" && {
- ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
- test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
- }
- }
- then posix_mkdir=:
- fi
- rmdir "$tmpdir/d" "$tmpdir"
- else
- # Remove any dirs left behind by ancient mkdir implementations.
- rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
- fi
- trap '' 0;;
- esac;;
+ trap '' 0;;
esac
if
$posix_mkdir && (
- umask $mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
- # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
- /*) prefix='/';;
- -*) prefix='./';;
- *) prefix='';;
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
esac
- eval "$initialize_posix_glob"
-
oIFS=$IFS
IFS=/
- $posix_glob set -f
+ set -f
set fnord $dstdir
shift
- $posix_glob set +f
+ set +f
IFS=$oIFS
prefixes=
for d
do
- test -z "$d" && continue
-
- prefix=$prefix$d
- if test -d "$prefix"; then
- prefixes=
- else
- if $posix_mkdir; then
- (umask=$mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
- # Don't fail if two instances are running concurrently.
- test -d "$prefix" || exit 1
- else
- case $prefix in
- *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
- *) qprefix=$prefix;;
- esac
- prefixes="$prefixes '$qprefix'"
- fi
- fi
- prefix=$prefix/
+ test X"$d" = X && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
done
if test -n "$prefixes"; then
- # Don't fail if two instances are running concurrently.
- (umask $mkdir_umask &&
- eval "\$doit_exec \$mkdirprog $prefixes") ||
- test -d "$dstdir" || exit 1
- obsolete_mkdir_used=true
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
fi
fi
fi
@@ -451,14 +449,25 @@ do
else
# Make a couple of temp file names in the proper directory.
- dsttmp=$dstdir/_inst.$$_
- rmtmp=$dstdir/_rm.$$_
+ dsttmp=${dstdirslash}_inst.$$_
+ rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
- (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+ (umask $cp_umask &&
+ { test -z "$stripcmd" || {
+ # Create $dsttmp read-write so that cp doesn't create it read-only,
+ # which would cause strip to fail.
+ if test -z "$doit"; then
+ : >"$dsttmp" # No need to fork-exec 'touch'.
+ else
+ $doit touch "$dsttmp"
+ fi
+ }
+ } &&
+ $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
@@ -473,20 +482,24 @@ do
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
- old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
- new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
- eval "$initialize_posix_glob" &&
- $posix_glob set -f &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
+ set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
+ # If $backupsuffix is set, and the file being installed
+ # already exists, attempt a backup. Don't worry if it fails,
+ # e.g., if mv doesn't support -f.
+ if test -n "$backupsuffix" && test -f "$dst"; then
+ $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+ fi
+
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
@@ -494,24 +507,24 @@ do
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
- # Now remove or move aside any old file at destination location.
- # We try this two ways since rm can't unlink itself on some
- # systems and the destination file might be busy for other
- # reasons. In this case, the final cleanup might fail but the new
- # file should still install successfully.
- {
- test ! -f "$dst" ||
- $doit $rmcmd -f "$dst" 2>/dev/null ||
- { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
- { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
- } ||
- { echo "$0: cannot unlink or rename $dst" >&2
- (exit 1); exit 1
- }
- } &&
-
- # Now rename the file to the real destination.
- $doit $mvcmd "$dsttmp" "$dst"
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
@@ -520,9 +533,9 @@ do
done
# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
+# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
diff --git a/sqlite/autoconf/tea/tclconfig/tcl.m4 b/sqlite/autoconf/tea/tclconfig/tcl.m4
index 4b4bd1e8..237d50a7 100644
--- a/sqlite/autoconf/tea/tclconfig/tcl.m4
+++ b/sqlite/autoconf/tea/tclconfig/tcl.m4
@@ -9,16 +9,13 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-AC_PREREQ(2.57)
-
-dnl TEA extensions pass us the version of TEA they think they
-dnl are compatible with (must be set in TEA_INIT below)
-dnl TEA_VERSION="3.9"
+AC_PREREQ([2.69])
# Possible values for key variables defined:
#
# TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem')
# TEA_PLATFORM - windows unix
+# TEA_TK_EXTENSION - True if this is a Tk extension
#
#------------------------------------------------------------------------
@@ -53,9 +50,13 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
# we reset no_tcl in case something fails here
no_tcl=true
AC_ARG_WITH(tcl,
- AC_HELP_STRING([--with-tcl],
+ AS_HELP_STRING([--with-tcl],
[directory containing tcl configuration (tclConfig.sh)]),
- with_tclconfig="${withval}")
+ [with_tclconfig="${withval}"])
+ AC_ARG_WITH(tcl8,
+ AS_HELP_STRING([--with-tcl8],
+ [Compile for Tcl8 in Tcl9 environment]),
+ [with_tcl8="${withval}"])
AC_MSG_CHECKING([for Tcl configuration])
AC_CACHE_VAL(ac_cv_c_tclconfig,[
@@ -107,7 +108,9 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
`ls -d /Library/Frameworks 2>/dev/null` \
`ls -d /Network/Library/Frameworks 2>/dev/null` \
- `ls -d /System/Library/Frameworks 2>/dev/null` \
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks/Tcl.framework 2>/dev/null` \
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Network/Library/Frameworks/Tcl.framework 2>/dev/null` \
+ `ls -d /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tcl.framework 2>/dev/null` \
; do
if test -f "$i/Tcl.framework/tclConfig.sh" ; then
ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`"
@@ -136,10 +139,21 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
`ls -d ${prefix}/lib 2>/dev/null` \
`ls -d /usr/local/lib 2>/dev/null` \
`ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/pkg/lib 2>/dev/null` \
`ls -d /usr/lib 2>/dev/null` \
`ls -d /usr/lib64 2>/dev/null` \
+ `ls -d /usr/lib/tcl9.0 2>/dev/null` \
+ `ls -d /usr/lib/tcl8.7 2>/dev/null` \
`ls -d /usr/lib/tcl8.6 2>/dev/null` \
`ls -d /usr/lib/tcl8.5 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl9.0 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl8.7 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl8.6 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl8.5 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tcl9.0 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tcl8.7 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tcl8.6 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tcl8.5 2>/dev/null` \
; do
if test -f "$i/tclConfig.sh" ; then
ac_cv_c_tclconfig="`(cd $i; pwd)`"
@@ -208,9 +222,9 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
# we reset no_tk in case something fails here
no_tk=true
AC_ARG_WITH(tk,
- AC_HELP_STRING([--with-tk],
+ AS_HELP_STRING([--with-tk],
[directory containing tk configuration (tkConfig.sh)]),
- with_tkconfig="${withval}")
+ [with_tkconfig="${withval}"])
AC_MSG_CHECKING([for Tk configuration])
AC_CACHE_VAL(ac_cv_c_tkconfig,[
@@ -262,7 +276,6 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
`ls -d /Library/Frameworks 2>/dev/null` \
`ls -d /Network/Library/Frameworks 2>/dev/null` \
- `ls -d /System/Library/Frameworks 2>/dev/null` \
; do
if test -f "$i/Tk.framework/tkConfig.sh" ; then
ac_cv_c_tkconfig="`(cd $i/Tk.framework; pwd)`"
@@ -278,8 +291,21 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
`ls -d ${prefix}/lib 2>/dev/null` \
`ls -d /usr/local/lib 2>/dev/null` \
`ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/pkg/lib 2>/dev/null` \
+ `ls -d /usr/lib/tk9.0 2>/dev/null` \
+ `ls -d /usr/lib/tk8.7 2>/dev/null` \
+ `ls -d /usr/lib/tk8.6 2>/dev/null` \
+ `ls -d /usr/lib/tk8.5 2>/dev/null` \
`ls -d /usr/lib 2>/dev/null` \
`ls -d /usr/lib64 2>/dev/null` \
+ `ls -d /usr/local/lib/tk9.0 2>/dev/null` \
+ `ls -d /usr/local/lib/tk8.7 2>/dev/null` \
+ `ls -d /usr/local/lib/tk8.6 2>/dev/null` \
+ `ls -d /usr/local/lib/tk8.5 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tk9.0 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tk8.7 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tk8.6 2>/dev/null` \
+ `ls -d /usr/local/lib/tcl/tk8.5 2>/dev/null` \
; do
if test -f "$i/tkConfig.sh" ; then
ac_cv_c_tkconfig="`(cd $i; pwd)`"
@@ -348,22 +374,20 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
# TCL_BIN_DIR
# TCL_SRC_DIR
# TCL_LIB_FILE
+# TCL_ZIP_FILE
+# TCL_ZIPFS_SUPPORT
#------------------------------------------------------------------------
AC_DEFUN([TEA_LOAD_TCLCONFIG], [
AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh])
if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then
- AC_MSG_RESULT([loading])
+ AC_MSG_RESULT([loading])
. "${TCL_BIN_DIR}/tclConfig.sh"
else
- AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh])
+ AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh])
fi
- # eval is required to do the TCL_DBGX substitution
- eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
- eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
-
# If the TCL_BIN_DIR is the build directory (not the install directory),
# then set the common variable name to the value of the build variables.
# For example, the variable TCL_LIB_SPEC will be set to the value
@@ -371,9 +395,9 @@ AC_DEFUN([TEA_LOAD_TCLCONFIG], [
# instead of TCL_BUILD_LIB_SPEC since it will work with both an
# installed and uninstalled version of Tcl.
if test -f "${TCL_BIN_DIR}/Makefile" ; then
- TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}"
- TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}"
- TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}"
+ TCL_LIB_SPEC="${TCL_BUILD_LIB_SPEC}"
+ TCL_STUB_LIB_SPEC="${TCL_BUILD_STUB_LIB_SPEC}"
+ TCL_STUB_LIB_PATH="${TCL_BUILD_STUB_LIB_PATH}"
elif test "`uname -s`" = "Darwin"; then
# If Tcl was built as a framework, attempt to use the libraries
# from the framework at the given location so that linking works
@@ -397,12 +421,6 @@ AC_DEFUN([TEA_LOAD_TCLCONFIG], [
esac
fi
- # eval is required to do the TCL_DBGX substitution
- eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
- eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
- eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
- eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
-
AC_SUBST(TCL_VERSION)
AC_SUBST(TCL_PATCH_LEVEL)
AC_SUBST(TCL_BIN_DIR)
@@ -418,13 +436,18 @@ AC_DEFUN([TEA_LOAD_TCLCONFIG], [
AC_MSG_CHECKING([platform])
hold_cc=$CC; CC="$TCL_CC"
- AC_TRY_COMPILE(,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
#ifdef _WIN32
#error win32
#endif
- ], TEA_PLATFORM="unix",
+ ]])],[
+ # first test we've already retrieved platform (cross-compile), fallback to unix otherwise:
+ TEA_PLATFORM="${TEA_PLATFORM-unix}"
+ CYGPATH=echo
+ ],[
TEA_PLATFORM="windows"
- )
+ AC_CHECK_PROG(CYGPATH, cygpath, cygpath -m, echo)
+ ])
CC=$hold_cc
AC_MSG_RESULT($TEA_PLATFORM)
@@ -467,16 +490,12 @@ AC_DEFUN([TEA_LOAD_TKCONFIG], [
AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh])
if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then
- AC_MSG_RESULT([loading])
+ AC_MSG_RESULT([loading])
. "${TK_BIN_DIR}/tkConfig.sh"
else
- AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh])
+ AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh])
fi
- # eval is required to do the TK_DBGX substitution
- eval "TK_LIB_FILE=\"${TK_LIB_FILE}\""
- eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\""
-
# If the TK_BIN_DIR is the build directory (not the install directory),
# then set the common variable name to the value of the build variables.
# For example, the variable TK_LIB_SPEC will be set to the value
@@ -484,9 +503,9 @@ AC_DEFUN([TEA_LOAD_TKCONFIG], [
# instead of TK_BUILD_LIB_SPEC since it will work with both an
# installed and uninstalled version of Tcl.
if test -f "${TK_BIN_DIR}/Makefile" ; then
- TK_LIB_SPEC="${TK_BUILD_LIB_SPEC}"
- TK_STUB_LIB_SPEC="${TK_BUILD_STUB_LIB_SPEC}"
- TK_STUB_LIB_PATH="${TK_BUILD_STUB_LIB_PATH}"
+ TK_LIB_SPEC="${TK_BUILD_LIB_SPEC}"
+ TK_STUB_LIB_SPEC="${TK_BUILD_STUB_LIB_SPEC}"
+ TK_STUB_LIB_PATH="${TK_BUILD_STUB_LIB_PATH}"
elif test "`uname -s`" = "Darwin"; then
# If Tk was built as a framework, attempt to use the libraries
# from the framework at the given location so that linking works
@@ -510,12 +529,6 @@ AC_DEFUN([TEA_LOAD_TKCONFIG], [
esac
fi
- # eval is required to do the TK_DBGX substitution
- eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\""
- eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\""
- eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\""
- eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\""
-
# TEA specific: Ensure windowingsystem is defined
if test "${TEA_PLATFORM}" = "unix" ; then
case ${TK_DEFS} in
@@ -570,29 +583,37 @@ AC_DEFUN([TEA_LOAD_TKCONFIG], [
AC_DEFUN([TEA_PROG_TCLSH], [
AC_MSG_CHECKING([for tclsh])
if test -f "${TCL_BIN_DIR}/Makefile" ; then
- # tclConfig.sh is in Tcl build directory
- if test "${TEA_PLATFORM}" = "windows"; then
- TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
- else
- TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
- fi
+ # tclConfig.sh is in Tcl build directory
+ if test "${TEA_PLATFORM}" = "windows"; then
+ if test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${EXEEXT}" ; then
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${EXEEXT}"
+ elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}s${EXEEXT}" ; then
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}s${EXEEXT}"
+ elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}t${EXEEXT}" ; then
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}t${EXEEXT}"
+ elif test -f "${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}st${EXEEXT}" ; then
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}st${EXEEXT}"
+ fi
+ else
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
+ fi
else
- # tclConfig.sh is in install location
- if test "${TEA_PLATFORM}" = "windows"; then
- TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
- else
- TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}"
- fi
- list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \
- `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \
- `ls -d ${TCL_PREFIX}/bin 2>/dev/null`"
- for i in $list ; do
- if test -f "$i/${TCLSH_PROG}" ; then
- REAL_TCL_BIN_DIR="`cd "$i"; pwd`/"
- break
- fi
- done
- TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}"
+ # tclConfig.sh is in install location
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${EXEEXT}"
+ else
+ TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}"
+ fi
+ list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`"
+ for i in $list ; do
+ if test -f "$i/${TCLSH_PROG}" ; then
+ REAL_TCL_BIN_DIR="`cd "$i"; pwd`/"
+ break
+ fi
+ done
+ TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}"
fi
AC_MSG_RESULT([${TCLSH_PROG}])
AC_SUBST(TCLSH_PROG)
@@ -620,29 +641,37 @@ AC_DEFUN([TEA_PROG_TCLSH], [
AC_DEFUN([TEA_PROG_WISH], [
AC_MSG_CHECKING([for wish])
if test -f "${TK_BIN_DIR}/Makefile" ; then
- # tkConfig.sh is in Tk build directory
- if test "${TEA_PLATFORM}" = "windows"; then
- WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
- else
- WISH_PROG="${TK_BIN_DIR}/wish"
- fi
+ # tkConfig.sh is in Tk build directory
+ if test "${TEA_PLATFORM}" = "windows"; then
+ if test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${EXEEXT}" ; then
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${EXEEXT}"
+ elif test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}s${EXEEXT}" ; then
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}$s{EXEEXT}"
+ elif test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}t${EXEEXT}" ; then
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}t${EXEEXT}"
+ elif test -f "${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}st${EXEEXT}" ; then
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}st${EXEEXT}"
+ fi
+ else
+ WISH_PROG="${TK_BIN_DIR}/wish"
+ fi
else
- # tkConfig.sh is in install location
- if test "${TEA_PLATFORM}" = "windows"; then
- WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
- else
- WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}${TK_DBGX}"
- fi
- list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \
- `ls -d ${TK_BIN_DIR}/.. 2>/dev/null` \
- `ls -d ${TK_PREFIX}/bin 2>/dev/null`"
- for i in $list ; do
- if test -f "$i/${WISH_PROG}" ; then
- REAL_TK_BIN_DIR="`cd "$i"; pwd`/"
- break
- fi
- done
- WISH_PROG="${REAL_TK_BIN_DIR}${WISH_PROG}"
+ # tkConfig.sh is in install location
+ if test "${TEA_PLATFORM}" = "windows"; then
+ WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${EXEEXT}"
+ else
+ WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}"
+ fi
+ list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \
+ `ls -d ${TK_BIN_DIR}/.. 2>/dev/null` \
+ `ls -d ${TK_PREFIX}/bin 2>/dev/null`"
+ for i in $list ; do
+ if test -f "$i/${WISH_PROG}" ; then
+ REAL_TK_BIN_DIR="`cd "$i"; pwd`/"
+ break
+ fi
+ done
+ WISH_PROG="${REAL_TK_BIN_DIR}${WISH_PROG}"
fi
AC_MSG_RESULT([${WISH_PROG}])
AC_SUBST(WISH_PROG)
@@ -660,6 +689,7 @@ AC_DEFUN([TEA_PROG_WISH], [
#
# Adds the following arguments to configure:
# --enable-shared=yes|no
+# --enable-stubs=yes|no
#
# Defines the following vars:
# STATIC_BUILD Used for building import/export libraries
@@ -667,31 +697,63 @@ AC_DEFUN([TEA_PROG_WISH], [
#
# Sets the following vars:
# SHARED_BUILD Value of 1 or 0
+# STUBS_BUILD Value if 1 or 0
+# USE_TCL_STUBS Value true: if SHARED_BUILD or --enable-stubs
+# USE_TCLOO_STUBS Value true: if SHARED_BUILD or --enable-stubs
+# USE_TK_STUBS Value true: if SHARED_BUILD or --enable-stubs
+# AND TEA_WINDOWING_SYSTEM != ""
#------------------------------------------------------------------------
-
AC_DEFUN([TEA_ENABLE_SHARED], [
AC_MSG_CHECKING([how to build libraries])
AC_ARG_ENABLE(shared,
- AC_HELP_STRING([--enable-shared],
+ AS_HELP_STRING([--enable-shared],
[build and link with shared libraries (default: on)]),
- [tcl_ok=$enableval], [tcl_ok=yes])
+ [shared_ok=$enableval], [shared_ok=yes])
if test "${enable_shared+set}" = set; then
enableval="$enable_shared"
- tcl_ok=$enableval
+ shared_ok=$enableval
else
- tcl_ok=yes
+ shared_ok=yes
+ fi
+
+ AC_ARG_ENABLE(stubs,
+ AS_HELP_STRING([--enable-stubs],
+ [build and link with stub libraries. Always true for shared builds (default: on)]),
+ [stubs_ok=$enableval], [stubs_ok=yes])
+
+ if test "${enable_stubs+set}" = set; then
+ enableval="$enable_stubs"
+ stubs_ok=$enableval
+ else
+ stubs_ok=yes
fi
- if test "$tcl_ok" = "yes" ; then
+ # Stubs are always enabled for shared builds
+ if test "$shared_ok" = "yes" ; then
AC_MSG_RESULT([shared])
SHARED_BUILD=1
+ STUBS_BUILD=1
else
AC_MSG_RESULT([static])
SHARED_BUILD=0
- AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?])
+ AC_DEFINE(STATIC_BUILD, 1, [This a static build])
+ if test "$stubs_ok" = "yes" ; then
+ STUBS_BUILD=1
+ else
+ STUBS_BUILD=0
+ fi
+ fi
+ if test "${STUBS_BUILD}" = "1" ; then
+ AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs])
+ AC_DEFINE(USE_TCLOO_STUBS, 1, [Use TclOO stubs])
+ if test "${TEA_WINDOWINGSYSTEM}" != ""; then
+ AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs])
+ fi
fi
+
AC_SUBST(SHARED_BUILD)
+ AC_SUBST(STUBS_BUILD)
])
#------------------------------------------------------------------------
@@ -728,8 +790,8 @@ AC_DEFUN([TEA_ENABLE_SHARED], [
AC_DEFUN([TEA_ENABLE_THREADS], [
AC_ARG_ENABLE(threads,
- AC_HELP_STRING([--enable-threads],
- [build with threads]),
+ AS_HELP_STRING([--enable-threads],
+ [build with threads (default: on)]),
[tcl_ok=$enableval], [tcl_ok=yes])
if test "${enable_threads+set}" = set; then
@@ -813,14 +875,6 @@ AC_DEFUN([TEA_ENABLE_THREADS], [
that IS thread-enabled. It is recommended to use --enable-threads.])
fi
;;
- *)
- if test "${TCL_THREADS}" = "1"; then
- AC_MSG_WARN([
- --enable-threads requested, but building against a Tcl that is NOT
- thread-enabled. This is an OK configuration that will also run in
- a thread-enabled core.])
- fi
- ;;
esac
AC_SUBST(TCL_THREADS)
])
@@ -850,8 +904,6 @@ AC_DEFUN([TEA_ENABLE_THREADS], [
# Sets to "$(CFLAGS_OPTIMIZE) -DNDEBUG" if false
# LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true
# Sets to $(LDFLAGS_OPTIMIZE) if false
-# DBGX Formerly used as debug library extension;
-# always blank now.
#------------------------------------------------------------------------
AC_DEFUN([TEA_ENABLE_SYMBOLS], [
@@ -859,14 +911,14 @@ AC_DEFUN([TEA_ENABLE_SYMBOLS], [
AC_REQUIRE([TEA_CONFIG_CFLAGS])
AC_MSG_CHECKING([for build with symbols])
AC_ARG_ENABLE(symbols,
- AC_HELP_STRING([--enable-symbols],
+ AS_HELP_STRING([--enable-symbols],
[build with debugging symbols (default: off)]),
[tcl_ok=$enableval], [tcl_ok=no])
- DBGX=""
if test "$tcl_ok" = "no"; then
CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG"
LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}"
AC_MSG_RESULT([no])
+ AC_DEFINE(TCL_CFG_OPTIMIZED, 1, [Is this an optimized build?])
else
CFLAGS_DEFAULT="${CFLAGS_DEBUG}"
LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}"
@@ -874,13 +926,8 @@ AC_DEFUN([TEA_ENABLE_SYMBOLS], [
AC_MSG_RESULT([yes (standard debugging)])
fi
fi
- # TEA specific:
- if test "${TEA_PLATFORM}" != "windows" ; then
- LDFLAGS_DEFAULT="${LDFLAGS}"
- fi
AC_SUBST(CFLAGS_DEFAULT)
AC_SUBST(LDFLAGS_DEFAULT)
- AC_SUBST(TCL_DBGX)
if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then
AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?])
@@ -915,7 +962,7 @@ AC_DEFUN([TEA_ENABLE_SYMBOLS], [
AC_DEFUN([TEA_ENABLE_LANGINFO], [
AC_ARG_ENABLE(langinfo,
- AC_HELP_STRING([--enable-langinfo],
+ AS_HELP_STRING([--enable-langinfo],
[use nl_langinfo if possible to determine encoding at startup, otherwise use old heuristic (default: on)]),
[langinfo_ok=$enableval], [langinfo_ok=yes])
@@ -926,7 +973,7 @@ AC_DEFUN([TEA_ENABLE_LANGINFO], [
AC_MSG_CHECKING([whether to use nl_langinfo])
if test "$langinfo_ok" = "yes"; then
AC_CACHE_VAL(tcl_cv_langinfo_h, [
- AC_TRY_COMPILE([#include ], [nl_langinfo(CODESET);],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[nl_langinfo(CODESET);]])],
[tcl_cv_langinfo_h=yes],[tcl_cv_langinfo_h=no])])
AC_MSG_RESULT([$tcl_cv_langinfo_h])
if test $tcl_cv_langinfo_h = yes; then
@@ -951,6 +998,7 @@ AC_DEFUN([TEA_ENABLE_LANGINFO], [
# Defines the following var:
#
# system - System/platform/version identification code.
+#
#--------------------------------------------------------------------
AC_DEFUN([TEA_CONFIG_SYSTEM], [
@@ -967,6 +1015,9 @@ AC_DEFUN([TEA_CONFIG_SYSTEM], [
if test "`uname -s`" = "AIX" ; then
tcl_cv_sys_version=AIX-`uname -v`.`uname -r`
fi
+ if test "`uname -s`" = "NetBSD" -a -f /etc/debian_version ; then
+ tcl_cv_sys_version=NetBSD-Debian
+ fi
fi
fi
])
@@ -1043,7 +1094,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_MSG_CHECKING([if 64bit support is requested])
AC_ARG_ENABLE(64bit,
- AC_HELP_STRING([--enable-64bit],
+ AS_HELP_STRING([--enable-64bit],
[enable 64bit support (default: off)]),
[do64bit=$enableval], [do64bit=no])
AC_MSG_RESULT([$do64bit])
@@ -1052,7 +1103,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_MSG_CHECKING([if 64bit Sparc VIS support is requested])
AC_ARG_ENABLE(64bit-vis,
- AC_HELP_STRING([--enable-64bit-vis],
+ AS_HELP_STRING([--enable-64bit-vis],
[enable 64bit Sparc VIS support (default: off)]),
[do64bitVIS=$enableval], [do64bitVIS=no])
AC_MSG_RESULT([$do64bitVIS])
@@ -1065,10 +1116,10 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_CACHE_CHECK([if compiler supports visibility "hidden"],
tcl_cv_cc_visibility_hidden, [
hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror"
- AC_TRY_LINK([
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
extern __attribute__((__visibility__("hidden"))) void f(void);
- void f(void) {}], [f();], tcl_cv_cc_visibility_hidden=yes,
- tcl_cv_cc_visibility_hidden=no)
+ void f(void) {}]], [[f();]])],[tcl_cv_cc_visibility_hidden=yes],
+ [tcl_cv_cc_visibility_hidden=no])
CFLAGS=$hold_cflags])
AS_IF([test $tcl_cv_cc_visibility_hidden = yes], [
AC_DEFINE(MODULE_SCOPE,
@@ -1081,22 +1132,11 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_MSG_CHECKING([if rpath support is requested])
AC_ARG_ENABLE(rpath,
- AC_HELP_STRING([--disable-rpath],
+ AS_HELP_STRING([--disable-rpath],
[disable rpath support (default: on)]),
[doRpath=$enableval], [doRpath=yes])
AC_MSG_RESULT([$doRpath])
- # TEA specific: Cross-compiling options for Windows/CE builds?
-
- AS_IF([test "${TEA_PLATFORM}" = windows], [
- AC_MSG_CHECKING([if Windows/CE build is requested])
- AC_ARG_ENABLE(wince,
- AC_HELP_STRING([--enable-wince],
- [enable Win/CE support (where applicable)]),
- [doWince=$enableval], [doWince=no])
- AC_MSG_RESULT([$doWince])
- ])
-
# Set the variable "system" to hold the name and version number
# for the system.
@@ -1133,149 +1173,53 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_CHECK_TOOL(AR, ar)
STLIB_LD='${AR} cr'
LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
- AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"])
+ AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION=""],[SHLIB_VERSION=".$SHLIB_VERSION"])
case $system in
# TEA specific:
windows)
- # This is a 2-stage check to make sure we have the 64-bit SDK
- # We have to know where the SDK is installed.
- # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs
- # MACHINE is IX86 for LINK, but this is used by the manifest,
- # which requires x86|amd64|ia64.
MACHINE="X86"
if test "$do64bit" != "no" ; then
- if test "x${MSSDK}x" = "xx" ; then
- MSSDK="C:/Progra~1/Microsoft Platform SDK"
- fi
- MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'`
- PATH64=""
case "$do64bit" in
amd64|x64|yes)
MACHINE="AMD64" ; # default to AMD64 64-bit build
- PATH64="${MSSDK}/Bin/Win64/x86/AMD64"
+ ;;
+ arm64|aarch64)
+ MACHINE="ARM64"
;;
ia64)
MACHINE="IA64"
- PATH64="${MSSDK}/Bin/Win64"
;;
esac
- if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then
- AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode])
- AC_MSG_WARN([Ensure latest Platform SDK is installed])
- do64bit="no"
- else
- AC_MSG_RESULT([ Using 64-bit $MACHINE mode])
- do64bit_ok="yes"
- fi
- fi
-
- if test "$doWince" != "no" ; then
- if test "$do64bit" != "no" ; then
- AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible])
- fi
- if test "$GCC" = "yes" ; then
- AC_MSG_ERROR([Windows/CE and GCC builds incompatible])
- fi
- TEA_PATH_CELIB
- # Set defaults for common evc4/PPC2003 setup
- # Currently Tcl requires 300+, possibly 420+ for sockets
- CEVERSION=420; # could be 211 300 301 400 420 ...
- TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ...
- ARCH=ARM; # could be ARM MIPS X86EM ...
- PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002"
- if test "$doWince" != "yes"; then
- # If !yes then the user specified something
- # Reset ARCH to allow user to skip specifying it
- ARCH=
- eval `echo $doWince | awk -F, '{ \
- if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \
- if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \
- if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \
- if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \
- if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \
- }'`
- if test "x${ARCH}" = "x" ; then
- ARCH=$TARGETCPU;
- fi
- fi
- OSVERSION=WCE$CEVERSION;
- if test "x${WCEROOT}" = "x" ; then
- WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0"
- if test ! -d "${WCEROOT}" ; then
- WCEROOT="C:/Program Files/Microsoft eMbedded Tools"
- fi
- fi
- if test "x${SDKROOT}" = "x" ; then
- SDKROOT="C:/Program Files/Windows CE Tools"
- if test ! -d "${SDKROOT}" ; then
- SDKROOT="C:/Windows CE Tools"
- fi
- fi
- WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'`
- SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'`
- if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \
- -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then
- AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]])
- doWince="no"
- else
- # We could PATH_NOSPACE these, but that's not important,
- # as long as we quote them when used.
- CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include"
- if test -d "${CEINCLUDE}/${TARGETCPU}" ; then
- CEINCLUDE="${CEINCLUDE}/${TARGETCPU}"
- fi
- CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"
- fi
fi
if test "$GCC" != "yes" ; then
- if test "${SHARED_BUILD}" = "0" ; then
+ if test "${SHARED_BUILD}" = "0" ; then
runtime=-MT
- else
+ else
runtime=-MD
- fi
-
- if test "$do64bit" != "no" ; then
- # All this magic is necessary for the Win64 SDK RC1 - hobbs
- CC="\"${PATH64}/cl.exe\""
- CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\""
- RC="\"${MSSDK}/bin/rc.exe\""
- lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
- LINKBIN="\"${PATH64}/link.exe\""
+ fi
+ case "x`echo \${VisualStudioVersion}`" in
+ x1[[4-9]]*)
+ lflags="${lflags} -nodefaultlib:libucrt.lib"
+ TEA_ADD_LIBS([ucrt.lib])
+ ;;
+ *)
+ ;;
+ esac
+
+ if test "$do64bit" != "no" ; then
+ CC="cl.exe"
+ RC="rc.exe"
+ lflags="${lflags} -nologo -MACHINE:${MACHINE} "
+ LINKBIN="link.exe"
CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d"
CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
# Avoid 'unresolved external symbol __security_cookie'
# errors, c.f. http://support.microsoft.com/?id=894573
TEA_ADD_LIBS([bufferoverflowU.lib])
- elif test "$doWince" != "no" ; then
- CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
- if test "${TARGETCPU}" = "X86"; then
- CC="\"${CEBINROOT}/cl.exe\""
- else
- CC="\"${CEBINROOT}/cl${ARCH}.exe\""
- fi
- CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\""
- RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\""
- arch=`echo ${ARCH} | awk '{print tolower([$]0)}'`
- defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS"
- if test "${SHARED_BUILD}" = "1" ; then
- # Static CE builds require static celib as well
- defs="${defs} _DLL"
- fi
- for i in $defs ; do
- AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i)
- done
- AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version])
- AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version])
- CFLAGS_DEBUG="-nologo -Zi -Od"
- CFLAGS_OPTIMIZE="-nologo -Ox"
- lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'`
- lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo"
- LINKBIN="\"${CEBINROOT}/link.exe\""
- AC_SUBST(CELIB_DIR)
else
RC="rc"
- lflags="-nologo"
+ lflags="${lflags} -nologo"
LINKBIN="link"
CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
@@ -1294,25 +1238,32 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_CACHE_CHECK(for cross-compile version of gcc,
ac_cv_cross,
- AC_TRY_COMPILE([
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#ifdef _WIN32
#error cross-compiler
#endif
- ], [],
- ac_cv_cross=yes,
- ac_cv_cross=no)
+ ]], [[]])],
+ [ac_cv_cross=yes],
+ [ac_cv_cross=no])
)
if test "$ac_cv_cross" = "yes"; then
case "$do64bit" in
amd64|x64|yes)
- CC="x86_64-w64-mingw32-gcc"
+ CC="x86_64-w64-mingw32-${CC}"
LD="x86_64-w64-mingw32-ld"
AR="x86_64-w64-mingw32-ar"
RANLIB="x86_64-w64-mingw32-ranlib"
RC="x86_64-w64-mingw32-windres"
;;
+ arm64|aarch64)
+ CC="aarch64-w64-mingw32-clang"
+ LD="aarch64-w64-mingw32-ld"
+ AR="aarch64-w64-mingw32-ar"
+ RANLIB="aarch64-w64-mingw32-ranlib"
+ RC="aarch64-w64-mingw32-windres"
+ ;;
*)
- CC="i686-w64-mingw32-gcc"
+ CC="i686-w64-mingw32-${CC}"
LD="i686-w64-mingw32-ld"
AR="i686-w64-mingw32-ar"
RANLIB="i686-w64-mingw32-ranlib"
@@ -1334,13 +1285,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
# This essentially turns it all on.
LDFLAGS_DEBUG="-debug -debugtype:cv"
LDFLAGS_OPTIMIZE="-release"
- if test "$doWince" != "no" ; then
- LDFLAGS_CONSOLE="-link ${lflags}"
- LDFLAGS_WINDOW=${LDFLAGS_CONSOLE}
- else
- LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
- LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
- fi
+ LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
+ LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
fi
SHLIB_SUFFIX=".dll"
@@ -1349,7 +1295,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
TCL_LIB_VERSIONS_OK=nodots
;;
AIX-*)
- AS_IF([test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"], [
+ AS_IF([test "$GCC" != "yes"], [
# AIX requires the _r compiler when gcc isn't being used
case "${CC}" in
*_r|*_r\ *)
@@ -1386,11 +1332,11 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
# AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC
SHLIB_LD="/usr/ccs/bin/ld -G -z text"
AS_IF([test "$GCC" = yes], [
- CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-Wl,-R,${LIB_RUNTIME_DIR}"'
], [
- CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-R${LIB_RUNTIME_DIR}"'
])
- LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-R "${LIB_RUNTIME_DIR}"'
], [
AS_IF([test "$GCC" = yes], [
SHLIB_LD='${CC} -shared -Wl,-bexpall'
@@ -1399,7 +1345,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
LDFLAGS="$LDFLAGS -brtl"
])
SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}"
- CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-L${LIB_RUNTIME_DIR}"'
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
])
;;
@@ -1415,6 +1361,13 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
#-----------------------------------------------------------
AC_CHECK_LIB(bind, inet_ntoa, [LIBS="$LIBS -lbind -lsocket"])
;;
+ BSD/OS-2.1*|BSD/OS-3*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="shlicc -r"
+ SHLIB_SUFFIX=".so"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
BSD/OS-4.*)
SHLIB_CFLAGS="-export-dynamic -fPIC"
SHLIB_LD='${CC} -shared'
@@ -1427,16 +1380,25 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
SHLIB_CFLAGS=""
SHLIB_LD='${CC} -shared'
SHLIB_SUFFIX=".dll"
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -Wl,--out-implib,\$[@].a"
EXEEXT=".exe"
do64bit_ok=yes
CC_SEARCH_FLAGS=""
LD_SEARCH_FLAGS=""
;;
+ dgux*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD='${CC} -G'
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
Haiku*)
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
SHLIB_CFLAGS="-fPIC"
SHLIB_SUFFIX=".so"
- SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}'
+ SHLIB_LD='${CC} ${CFLAGS} ${LDFLAGS} -shared'
AC_CHECK_LIB(network, inet_ntoa, [LIBS="$LIBS -lnetwork"])
;;
HP-UX-*.11.*)
@@ -1448,18 +1410,16 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AS_IF([test "`uname -m`" = ia64], [
SHLIB_SUFFIX=".so"
- # Use newer C++ library for C++ extensions
- #if test "$GCC" != "yes" ; then
- # CPPFLAGS="-AA"
- #fi
], [
SHLIB_SUFFIX=".sl"
])
AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
AS_IF([test "$tcl_ok" = yes], [
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld -b"
LDFLAGS="$LDFLAGS -Wl,-E"
- CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
- LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
+ CC_SEARCH_FLAGS='"-Wl,+s,+b,${LIB_RUNTIME_DIR}:."'
+ LD_SEARCH_FLAGS='+s +b "${LIB_RUNTIME_DIR}:."'
LD_LIBRARY_PATH_VAR="SHLIB_PATH"
])
AS_IF([test "$GCC" = yes], [
@@ -1467,10 +1427,6 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
], [
CFLAGS="$CFLAGS -z"
- # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc
- #CFLAGS="$CFLAGS +DAportable"
- SHLIB_CFLAGS="+z"
- SHLIB_LD="ld -b"
])
# Check to enable 64-bit flags for compiler/linker
@@ -1482,7 +1438,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
do64bit_ok=yes
SHLIB_LD='${CC} -shared'
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'])
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
;;
*)
@@ -1495,13 +1451,34 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
LDFLAGS_ARCH="+DD64"
])
]) ;;
+ HP-UX-*.08.*|HP-UX-*.09.*|HP-UX-*.10.*)
+ SHLIB_SUFFIX=".sl"
+ AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
+ AS_IF([test "$tcl_ok" = yes], [
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld -b"
+ SHLIB_LD_LIBS=""
+ LDFLAGS="$LDFLAGS -Wl,-E"
+ CC_SEARCH_FLAGS='"-Wl,+s,+b,${LIB_RUNTIME_DIR}:."'
+ LD_SEARCH_FLAGS='+s +b "${LIB_RUNTIME_DIR}:."'
+ LD_LIBRARY_PATH_VAR="SHLIB_PATH"
+ ]) ;;
+ IRIX-5.*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld -shared -rdata_shared"
+ SHLIB_SUFFIX=".so"
+ AC_LIBOBJ(mkstemp)
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'
+ LD_SEARCH_FLAGS='-rpath "${LIB_RUNTIME_DIR}"'])
+ ;;
IRIX-6.*)
SHLIB_CFLAGS=""
SHLIB_LD="ld -n32 -shared -rdata_shared"
SHLIB_SUFFIX=".so"
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
- LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'
+ LD_SEARCH_FLAGS='-rpath "${LIB_RUNTIME_DIR}"'])
AS_IF([test "$GCC" = yes], [
CFLAGS="$CFLAGS -mabi=n32"
LDFLAGS="$LDFLAGS -mabi=n32"
@@ -1523,23 +1500,23 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
SHLIB_LD="ld -n32 -shared -rdata_shared"
SHLIB_SUFFIX=".so"
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
- LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'
+ LD_SEARCH_FLAGS='-rpath "${LIB_RUNTIME_DIR}"'])
# Check to enable 64-bit flags for compiler/linker
AS_IF([test "$do64bit" = yes], [
- AS_IF([test "$GCC" = yes], [
- AC_MSG_WARN([64bit mode not supported by gcc])
- ], [
- do64bit_ok=yes
- SHLIB_LD="ld -64 -shared -rdata_shared"
- CFLAGS="$CFLAGS -64"
- LDFLAGS_ARCH="-64"
- ])
+ AS_IF([test "$GCC" = yes], [
+ AC_MSG_WARN([64bit mode not supported by gcc])
+ ], [
+ do64bit_ok=yes
+ SHLIB_LD="ld -64 -shared -rdata_shared"
+ CFLAGS="$CFLAGS -64"
+ LDFLAGS_ARCH="-64"
+ ])
])
;;
- Linux*|GNU*|NetBSD-Debian)
+ Linux*|GNU*|NetBSD-Debian|DragonFly-*|FreeBSD-*)
SHLIB_CFLAGS="-fPIC"
SHLIB_SUFFIX=".so"
@@ -1547,17 +1524,29 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
# TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
- SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}'
+ SHLIB_LD='${CC} ${CFLAGS} ${LDFLAGS_DEFAULT} -shared'
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+
+ case $system in
+ DragonFly-*|FreeBSD-*)
+ AS_IF([test "${TCL_THREADS}" = "1"], [
+ # The -pthread needs to go in the LDFLAGS, not LIBS
+ LIBS=`echo $LIBS | sed s/-pthread//`
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LDFLAGS="$LDFLAGS $PTHREAD_LIBS"])
+ ;;
+ esac
+
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'])
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"])
AS_IF([test $do64bit = yes], [
AC_CACHE_CHECK([if compiler accepts -m64 flag], tcl_cv_cc_m64, [
hold_cflags=$CFLAGS
CFLAGS="$CFLAGS -m64"
- AC_TRY_LINK(,, tcl_cv_cc_m64=yes, tcl_cv_cc_m64=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+ [tcl_cv_cc_m64=yes],[tcl_cv_cc_m64=no])
CFLAGS=$hold_cflags])
AS_IF([test $tcl_cv_cc_m64 = yes], [
CFLAGS="$CFLAGS -m64"
@@ -1580,42 +1569,31 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
SHLIB_LD='${CC} -shared'
LD_FLAGS="-Wl,--export-dynamic"
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
- LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'
+ LD_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'])
;;
OpenBSD-*)
arch=`arch -s`
case "$arch" in
- vax)
- SHLIB_SUFFIX=""
- SHARED_LIB_SUFFIX=""
- LDFLAGS=""
- ;;
- *)
+ alpha|sparc64)
SHLIB_CFLAGS="-fPIC"
- SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
- SHLIB_SUFFIX=".so"
- AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
- LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
- SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
- LDFLAGS="-Wl,-export-dynamic"
- ;;
- esac
- case "$arch" in
- vax)
- CFLAGS_OPTIMIZE="-O1"
;;
*)
- CFLAGS_OPTIMIZE="-O2"
+ SHLIB_CFLAGS="-fpic"
;;
esac
- AS_IF([test "${TCL_THREADS}" = "1"], [
- # On OpenBSD: Compile with -pthread
- # Don't link with -lpthread
- LIBS=`echo $LIBS | sed s/-lpthread//`
- CFLAGS="$CFLAGS -pthread"
- ])
+ SHLIB_LD='${CC} ${SHLIB_CFLAGS} -shared'
+ SHLIB_SUFFIX=".so"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'])
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so${SHLIB_VERSION}'
+ LDFLAGS="$LDFLAGS -Wl,-export-dynamic"
+ CFLAGS_OPTIMIZE="-O2"
+ # On OpenBSD: Compile with -pthread
+ # Don't link with -lpthread
+ LIBS=`echo $LIBS | sed s/-lpthread//`
+ CFLAGS="$CFLAGS -pthread"
# OpenBSD doesn't do version numbers with dots.
UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
TCL_LIB_VERSIONS_OK=nodots
@@ -1623,44 +1601,16 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
NetBSD-*)
# NetBSD has ELF and can use 'cc -shared' to build shared libs
SHLIB_CFLAGS="-fPIC"
- SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+ SHLIB_LD='${CC} ${SHLIB_CFLAGS} -shared'
SHLIB_SUFFIX=".so"
LDFLAGS="$LDFLAGS -export-dynamic"
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'])
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
- AS_IF([test "${TCL_THREADS}" = "1"], [
- # The -pthread needs to go in the CFLAGS, not LIBS
- LIBS=`echo $LIBS | sed s/-pthread//`
- CFLAGS="$CFLAGS -pthread"
- LDFLAGS="$LDFLAGS -pthread"
- ])
- ;;
- FreeBSD-*)
- # This configuration from FreeBSD Ports.
- SHLIB_CFLAGS="-fPIC"
- SHLIB_LD="${CC} -shared"
- TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$[@]"
- TK_SHLIB_LD_EXTRAS="-Wl,-soname,\$[@]"
- SHLIB_SUFFIX=".so"
- LDFLAGS=""
- AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
- LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
- AS_IF([test "${TCL_THREADS}" = "1"], [
- # The -pthread needs to go in the LDFLAGS, not LIBS
- LIBS=`echo $LIBS | sed s/-pthread//`
- CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
- LDFLAGS="$LDFLAGS $PTHREAD_LIBS"])
- case $system in
- FreeBSD-3.*)
- # Version numbers are dot-stripped by system policy.
- TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .`
- UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
- SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
- TCL_LIB_VERSIONS_OK=nodots
- ;;
- esac
+ # The -pthread needs to go in the CFLAGS, not LIBS
+ LIBS=`echo $LIBS | sed s/-pthread//`
+ CFLAGS="$CFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread"
;;
Darwin-*)
CFLAGS_OPTIMIZE="-Os"
@@ -1681,8 +1631,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
tcl_cv_cc_arch_ppc64, [
hold_cflags=$CFLAGS
CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
- AC_TRY_LINK(,, tcl_cv_cc_arch_ppc64=yes,
- tcl_cv_cc_arch_ppc64=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+ [tcl_cv_cc_arch_ppc64=yes],[tcl_cv_cc_arch_ppc64=no])
CFLAGS=$hold_cflags])
AS_IF([test $tcl_cv_cc_arch_ppc64 = yes], [
CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
@@ -1693,8 +1643,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
tcl_cv_cc_arch_x86_64, [
hold_cflags=$CFLAGS
CFLAGS="$CFLAGS -arch x86_64"
- AC_TRY_LINK(,, tcl_cv_cc_arch_x86_64=yes,
- tcl_cv_cc_arch_x86_64=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+ [tcl_cv_cc_arch_x86_64=yes],[tcl_cv_cc_arch_x86_64=no])
CFLAGS=$hold_cflags])
AS_IF([test $tcl_cv_cc_arch_x86_64 = yes], [
CFLAGS="$CFLAGS -arch x86_64"
@@ -1714,7 +1664,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_CACHE_CHECK([if ld accepts -single_module flag], tcl_cv_ld_single_module, [
hold_ldflags=$LDFLAGS
LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module"
- AC_TRY_LINK(, [int i;], tcl_cv_ld_single_module=yes, tcl_cv_ld_single_module=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[int i;]])],
+ [tcl_cv_ld_single_module=yes],[tcl_cv_ld_single_module=no])
LDFLAGS=$hold_ldflags])
AS_IF([test $tcl_cv_ld_single_module = yes], [
SHLIB_LD="${SHLIB_LD} -Wl,-single_module"
@@ -1723,17 +1674,13 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d`
SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}"
SHLIB_SUFFIX=".dylib"
- # Don't use -prebind when building for Mac OS X 10.4 or later only:
- AS_IF([test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int([$]2)}'`" -lt 4 -a \
- "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int([$]2)}'`" -lt 4], [
- LDFLAGS="$LDFLAGS -prebind"])
LDFLAGS="$LDFLAGS -headerpad_max_install_names"
AC_CACHE_CHECK([if ld accepts -search_paths_first flag],
tcl_cv_ld_search_paths_first, [
hold_ldflags=$LDFLAGS
LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
- AC_TRY_LINK(, [int i;], tcl_cv_ld_search_paths_first=yes,
- tcl_cv_ld_search_paths_first=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[int i;]])],
+ [tcl_cv_ld_search_paths_first=yes],[tcl_cv_ld_search_paths_first=no])
LDFLAGS=$hold_ldflags])
AS_IF([test $tcl_cv_ld_search_paths_first = yes], [
LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
@@ -1756,8 +1703,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
done
CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include"
LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11"
- AC_TRY_LINK([#include ], [XrmInitialize();],
- tcl_cv_lib_x11_64=yes, tcl_cv_lib_x11_64=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[XrmInitialize();]])],
+ [tcl_cv_lib_x11_64=yes],[tcl_cv_lib_x11_64=no])
for v in CFLAGS CPPFLAGS LDFLAGS; do
eval $v'="$hold_'$v'"'
done])
@@ -1769,8 +1716,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
done
CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}"
LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}"
- AC_TRY_LINK([#include ], [Tk_InitStubs(NULL, "", 0);],
- tcl_cv_lib_tk_64=yes, tcl_cv_lib_tk_64=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[Tk_InitStubs(NULL, "", 0);]])],
+ [tcl_cv_lib_tk_64=yes],[tcl_cv_lib_tk_64=no])
for v in CFLAGS CPPFLAGS LDFLAGS; do
eval $v'="$hold_'$v'"'
done])
@@ -1793,27 +1740,25 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
# Digital OSF/1
SHLIB_CFLAGS=""
AS_IF([test "$SHARED_BUILD" = 1], [
- SHLIB_LD='ld -shared -expect_unresolved "*"'
+ SHLIB_LD='ld -shared -expect_unresolved "*"'
], [
- SHLIB_LD='ld -non_shared -expect_unresolved "*"'
+ SHLIB_LD='ld -non_shared -expect_unresolved "*"'
])
SHLIB_SUFFIX=".so"
AS_IF([test $doRpath = yes], [
- CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"'
LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
AS_IF([test "$GCC" = yes], [CFLAGS="$CFLAGS -mieee"], [
CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"])
# see pthread_intro(3) for pthread support on osf1, k.furukawa
- AS_IF([test "${TCL_THREADS}" = 1], [
- CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE"
- CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64"
- LIBS=`echo $LIBS | sed s/-lpthreads//`
- AS_IF([test "$GCC" = yes], [
- LIBS="$LIBS -lpthread -lmach -lexc"
- ], [
- CFLAGS="$CFLAGS -pthread"
- LDFLAGS="$LDFLAGS -pthread"
- ])
+ CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE"
+ CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64"
+ LIBS=`echo $LIBS | sed s/-lpthreads//`
+ AS_IF([test "$GCC" = yes], [
+ LIBS="$LIBS -lpthread -lmach -lexc"
+ ], [
+ CFLAGS="$CFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread"
])
;;
QNX-6*)
@@ -1854,11 +1799,11 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
SHLIB_SUFFIX=".so"
AS_IF([test "$GCC" = yes], [
SHLIB_LD='${CC} -shared'
- CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-Wl,-R,${LIB_RUNTIME_DIR}"'
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
], [
SHLIB_LD="/usr/ccs/bin/ld -G -z text"
- CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='-R "${LIB_RUNTIME_DIR}"'
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
])
;;
@@ -1924,7 +1869,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
SHLIB_SUFFIX=".so"
AS_IF([test "$GCC" = yes], [
SHLIB_LD='${CC} -shared'
- CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-Wl,-R,${LIB_RUNTIME_DIR}"'
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
AS_IF([test "$do64bit_ok" = yes], [
AS_IF([test "$arch" = "sparcv9 sparc"], [
@@ -1951,8 +1896,8 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
*)
SHLIB_LD='/usr/ccs/bin/ld -G -z text';;
esac
- CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
- LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ CC_SEARCH_FLAGS='"-Wl,-R,${LIB_RUNTIME_DIR}"'
+ LD_SEARCH_FLAGS='-R "${LIB_RUNTIME_DIR}"'
])
;;
UNIX_SV* | UnixWare-5*)
@@ -1965,8 +1910,9 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
AC_CACHE_CHECK([for ld accepts -Bexport flag], tcl_cv_ld_Bexport, [
hold_ldflags=$LDFLAGS
LDFLAGS="$LDFLAGS -Wl,-Bexport"
- AC_TRY_LINK(, [int i;], tcl_cv_ld_Bexport=yes, tcl_cv_ld_Bexport=no)
- LDFLAGS=$hold_ldflags])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[int i;]])],
+ [tcl_cv_ld_Bexport=yes],[tcl_cv_ld_Bexport=no])
+ LDFLAGS=$hold_ldflags])
AS_IF([test $tcl_cv_ld_Bexport = yes], [
LDFLAGS="$LDFLAGS -Wl,-Bexport"
])
@@ -1997,9 +1943,9 @@ dnl # preprocessing tests use only CPPFLAGS.
case $system in
AIX-*) ;;
BSD/OS*) ;;
- CYGWIN_*|MINGW32_*) ;;
+ CYGWIN_*|MINGW32_*|MINGW64_*|MSYS_*) ;;
IRIX*) ;;
- NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
+ NetBSD-*|DragonFly-*|FreeBSD-*|OpenBSD-*) ;;
Darwin-*) ;;
SCO_SV-3.2*) ;;
windows) ;;
@@ -2021,7 +1967,7 @@ dnl # preprocessing tests use only CPPFLAGS.
if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then
AC_CACHE_CHECK(for SEH support in compiler,
tcl_cv_seh,
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#define WIN32_LEAN_AND_MEAN
#include
#undef WIN32_LEAN_AND_MEAN
@@ -2036,10 +1982,10 @@ dnl # preprocessing tests use only CPPFLAGS.
}
return 1;
}
- ],
- tcl_cv_seh=yes,
- tcl_cv_seh=no,
- tcl_cv_seh=no)
+ ]])],
+ [tcl_cv_seh=yes],
+ [tcl_cv_seh=no],
+ [tcl_cv_seh=no])
)
if test "$tcl_cv_seh" = "no" ; then
AC_DEFINE(HAVE_NO_SEH, 1,
@@ -2054,15 +2000,15 @@ dnl # preprocessing tests use only CPPFLAGS.
#
AC_CACHE_CHECK(for EXCEPTION_DISPOSITION support in include files,
tcl_cv_eh_disposition,
- AC_TRY_COMPILE([
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
# define WIN32_LEAN_AND_MEAN
# include
# undef WIN32_LEAN_AND_MEAN
- ],[
+ ]], [[
EXCEPTION_DISPOSITION x;
- ],
- tcl_cv_eh_disposition=yes,
- tcl_cv_eh_disposition=no)
+ ]])],
+ [tcl_cv_eh_disposition=yes],
+ [tcl_cv_eh_disposition=no])
)
if test "$tcl_cv_eh_disposition" = "no" ; then
AC_DEFINE(EXCEPTION_DISPOSITION, int,
@@ -2075,18 +2021,18 @@ dnl # preprocessing tests use only CPPFLAGS.
AC_CACHE_CHECK(for winnt.h that ignores VOID define,
tcl_cv_winnt_ignore_void,
- AC_TRY_COMPILE([
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#define VOID void
#define WIN32_LEAN_AND_MEAN
#include
#undef WIN32_LEAN_AND_MEAN
- ], [
+ ]], [[
CHAR c;
SHORT s;
LONG l;
- ],
- tcl_cv_winnt_ignore_void=yes,
- tcl_cv_winnt_ignore_void=no)
+ ]])],
+ [tcl_cv_winnt_ignore_void=yes],
+ [tcl_cv_winnt_ignore_void=no])
)
if test "$tcl_cv_winnt_ignore_void" = "yes" ; then
AC_DEFINE(HAVE_WINNT_IGNORE_VOID, 1,
@@ -2100,22 +2046,25 @@ dnl # preprocessing tests use only CPPFLAGS.
AC_CACHE_CHECK(for cast to union support,
tcl_cv_cast_to_union,
- AC_TRY_COMPILE([],
- [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
union foo { int i; double d; };
union foo f = (union foo) (int) 0;
- ],
- tcl_cv_cast_to_union=yes,
- tcl_cv_cast_to_union=no)
+ ]])],
+ [tcl_cv_cast_to_union=yes],
+ [tcl_cv_cast_to_union=no])
)
if test "$tcl_cv_cast_to_union" = "yes"; then
AC_DEFINE(HAVE_CAST_TO_UNION, 1,
[Defined when compiler supports casting to union type.])
fi
+ AC_CHECK_HEADER(stdbool.h, [AC_DEFINE(HAVE_STDBOOL_H, 1, [Do we have ?])],)
+
AC_SUBST(CFLAGS_DEBUG)
AC_SUBST(CFLAGS_OPTIMIZE)
AC_SUBST(CFLAGS_WARNING)
+ AC_SUBST(LDFLAGS_DEBUG)
+ AC_SUBST(LDFLAGS_OPTIMIZE)
AC_SUBST(STLIB_LD)
AC_SUBST(SHLIB_LD)
@@ -2155,7 +2104,7 @@ dnl # preprocessing tests use only CPPFLAGS.
AC_DEFUN([TEA_SERIAL_PORT], [
AC_CHECK_HEADERS(sys/modem.h)
AC_CACHE_CHECK([termios vs. termio vs. sgtty], tcl_cv_api_serial, [
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include
int main() {
@@ -2166,9 +2115,9 @@ int main() {
return 0;
}
return 1;
-}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+}]])],[tcl_cv_api_serial=termios],[tcl_cv_api_serial=no],[tcl_cv_api_serial=no])
if test $tcl_cv_api_serial = no ; then
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include
int main() {
@@ -2178,10 +2127,10 @@ int main() {
return 0;
}
return 1;
-}], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+}]])],[tcl_cv_api_serial=termio],[tcl_cv_api_serial=no],[tcl_cv_api_serial=no])
fi
if test $tcl_cv_api_serial = no ; then
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include
int main() {
@@ -2192,10 +2141,10 @@ int main() {
return 0;
}
return 1;
-}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+}]])],[tcl_cv_api_serial=sgtty],[tcl_cv_api_serial=no],[tcl_cv_api_serial=no])
fi
if test $tcl_cv_api_serial = no ; then
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include
#include
@@ -2208,10 +2157,10 @@ int main() {
return 0;
}
return 1;
-}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+}]])],[tcl_cv_api_serial=termios],[tcl_cv_api_serial=no],[tcl_cv_api_serial=no])
fi
if test $tcl_cv_api_serial = no; then
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include
#include
@@ -2223,10 +2172,10 @@ int main() {
return 0;
}
return 1;
- }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ }]])],[tcl_cv_api_serial=termio],[tcl_cv_api_serial=no],[tcl_cv_api_serial=no])
fi
if test $tcl_cv_api_serial = no; then
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include
#include
@@ -2239,7 +2188,7 @@ int main() {
return 0;
}
return 1;
-}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=none, tcl_cv_api_serial=none)
+}]])],[tcl_cv_api_serial=sgtty],[tcl_cv_api_serial=none],[tcl_cv_api_serial=none])
fi])
case $tcl_cv_api_serial in
termios) AC_DEFINE(USE_TERMIOS, 1, [Use the termios API for serial lines]);;
@@ -2248,97 +2197,6 @@ int main() {
esac
])
-#--------------------------------------------------------------------
-# TEA_MISSING_POSIX_HEADERS
-#
-# Supply substitutes for missing POSIX header files. Special
-# notes:
-# - stdlib.h doesn't define strtol, strtoul, or
-# strtod in some versions of SunOS
-# - some versions of string.h don't declare procedures such
-# as strstr
-#
-# Arguments:
-# none
-#
-# Results:
-#
-# Defines some of the following vars:
-# NO_DIRENT_H
-# NO_ERRNO_H
-# NO_VALUES_H
-# HAVE_LIMITS_H or NO_LIMITS_H
-# NO_STDLIB_H
-# NO_STRING_H
-# NO_SYS_WAIT_H
-# NO_DLFCN_H
-# HAVE_SYS_PARAM_H
-#
-# HAVE_STRING_H ?
-#
-# tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and
-# CHECK on limits.h
-#--------------------------------------------------------------------
-
-AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [
- AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h, [
- AC_TRY_LINK([#include
-#include ], [
-#ifndef _POSIX_SOURCE
-# ifdef __Lynx__
- /*
- * Generate compilation error to make the test fail: Lynx headers
- * are only valid if really in the POSIX environment.
- */
-
- missing_procedure();
-# endif
-#endif
-DIR *d;
-struct dirent *entryPtr;
-char *p;
-d = opendir("foobar");
-entryPtr = readdir(d);
-p = entryPtr->d_name;
-closedir(d);
-], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no)])
-
- if test $tcl_cv_dirent_h = no; then
- AC_DEFINE(NO_DIRENT_H, 1, [Do we have ?])
- fi
-
- # TEA specific:
- AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have ?])])
- AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have ?])])
- AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have ?])])
- AC_CHECK_HEADER(limits.h,
- [AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have ?])],
- [AC_DEFINE(NO_LIMITS_H, 1, [Do we have ?])])
- AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0)
- AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0)
- AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0)
- AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0)
- if test $tcl_ok = 0; then
- AC_DEFINE(NO_STDLIB_H, 1, [Do we have ?])
- fi
- AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0)
- AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0)
- AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0)
-
- # See also memmove check below for a place where NO_STRING_H can be
- # set and why.
-
- if test $tcl_ok = 0; then
- AC_DEFINE(NO_STRING_H, 1, [Do we have ?])
- fi
-
- AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have ?])])
- AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have ?])])
-
- # OS/390 lacks sys/param.h (and doesn't need it, by chance).
- AC_HAVE_HEADERS(sys/param.h)
-])
-
#--------------------------------------------------------------------
# TEA_PATH_X
#
@@ -2374,7 +2232,7 @@ AC_DEFUN([TEA_PATH_UNIX_X], [
not_really_there=""
if test "$no_x" = ""; then
if test "$x_includes" = ""; then
- AC_TRY_CPP([#include ], , not_really_there="yes")
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include ]])],[],[not_really_there="yes"])
else
if test ! -r $x_includes/X11/Xlib.h; then
not_really_there="yes"
@@ -2384,7 +2242,7 @@ AC_DEFUN([TEA_PATH_UNIX_X], [
if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then
AC_MSG_CHECKING([for X11 header files])
found_xincludes="no"
- AC_TRY_CPP([#include ], found_xincludes="yes", found_xincludes="no")
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include ]])],[found_xincludes="yes"],[found_xincludes="no"])
if test "$found_xincludes" = "no"; then
dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include"
for i in $dirs ; do
@@ -2490,6 +2348,7 @@ AC_DEFUN([TEA_BLOCKING_STYLE], [
# HAVE_TM_GMTOFF
# HAVE_TM_TZADJ
# HAVE_TIMEZONE_VAR
+#
#--------------------------------------------------------------------
AC_DEFUN([TEA_TIME_HANDLER], [
@@ -2497,18 +2356,20 @@ AC_DEFUN([TEA_TIME_HANDLER], [
AC_HEADER_TIME
AC_STRUCT_TIMEZONE
- AC_CHECK_FUNCS(gmtime_r localtime_r)
+ AC_CHECK_FUNCS(gmtime_r localtime_r mktime)
AC_CACHE_CHECK([tm_tzadj in struct tm], tcl_cv_member_tm_tzadj, [
- AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_tzadj;],
- tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no)])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct tm tm; (void)tm.tm_tzadj;]])],
+ [tcl_cv_member_tm_tzadj=yes],
+ [tcl_cv_member_tm_tzadj=no])])
if test $tcl_cv_member_tm_tzadj = yes ; then
AC_DEFINE(HAVE_TM_TZADJ, 1, [Should we use the tm_tzadj field of struct tm?])
fi
AC_CACHE_CHECK([tm_gmtoff in struct tm], tcl_cv_member_tm_gmtoff, [
- AC_TRY_COMPILE([#include ], [struct tm tm; tm.tm_gmtoff;],
- tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no)])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct tm tm; (void)tm.tm_gmtoff;]])],
+ [tcl_cv_member_tm_gmtoff=yes],
+ [tcl_cv_member_tm_gmtoff=no])])
if test $tcl_cv_member_tm_gmtoff = yes ; then
AC_DEFINE(HAVE_TM_GMTOFF, 1, [Should we use the tm_gmtoff field of struct tm?])
fi
@@ -2518,11 +2379,12 @@ AC_DEFUN([TEA_TIME_HANDLER], [
# (like convex) have timezone functions, etc.
#
AC_CACHE_CHECK([long timezone variable], tcl_cv_timezone_long, [
- AC_TRY_COMPILE([#include ],
- [extern long timezone;
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include
+#include ]],
+ [[extern long timezone;
timezone += 1;
- exit (0);],
- tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no)])
+ exit (0);]])],
+ [tcl_cv_timezone_long=yes], [tcl_cv_timezone_long=no])])
if test $tcl_cv_timezone_long = yes ; then
AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
else
@@ -2530,11 +2392,12 @@ AC_DEFUN([TEA_TIME_HANDLER], [
# On some systems (eg IRIX 6.2), timezone is a time_t and not a long.
#
AC_CACHE_CHECK([time_t timezone variable], tcl_cv_timezone_time, [
- AC_TRY_COMPILE([#include ],
- [extern time_t timezone;
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include
+#include ]],
+ [[extern time_t timezone;
timezone += 1;
- exit (0);],
- tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no)])
+ exit (0);]])],
+ [tcl_cv_timezone_time=yes], [tcl_cv_timezone_time=no])])
if test $tcl_cv_timezone_time = yes ; then
AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
fi
@@ -2564,7 +2427,8 @@ AC_DEFUN([TEA_BUGGY_STRTOD], [
AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0)
if test "$tcl_strtod" = 1; then
AC_CACHE_CHECK([for Solaris2.4/Tru64 strtod bugs], tcl_cv_strtod_buggy,[
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+ #include
extern double strtod();
int main() {
char *infString="Inf", *nanString="NaN", *spaceString=" ";
@@ -2583,8 +2447,8 @@ AC_DEFUN([TEA_BUGGY_STRTOD], [
exit(1);
}
exit(0);
- }], tcl_cv_strtod_buggy=ok, tcl_cv_strtod_buggy=buggy,
- tcl_cv_strtod_buggy=buggy)])
+ }]])], [tcl_cv_strtod_buggy=ok], [tcl_cv_strtod_buggy=buggy],
+ [tcl_cv_strtod_buggy=buggy])])
if test "$tcl_cv_strtod_buggy" = buggy; then
AC_LIBOBJ([fixstrtod])
USE_COMPAT=1
@@ -2597,38 +2461,30 @@ AC_DEFUN([TEA_BUGGY_STRTOD], [
# TEA_TCL_LINK_LIBS
#
# Search for the libraries needed to link the Tcl shell.
-# Things like the math library (-lm) and socket stuff (-lsocket vs.
-# -lnsl) are dealt with here.
+# Things like the math library (-lm), socket stuff (-lsocket vs.
+# -lnsl), zlib (-lz) and libtommath (-ltommath) are dealt with here.
#
# Arguments:
-# Requires the following vars to be set in the Makefile:
-# DL_LIBS (not in TEA, only needed in core)
-# LIBS
-# MATH_LIBS
+# None.
#
# Results:
#
-# Substitutes the following vars:
-# TCL_LIBS
-# MATH_LIBS
-#
# Might append to the following vars:
# LIBS
+# MATH_LIBS
#
# Might define the following vars:
# HAVE_NET_ERRNO_H
+#
#--------------------------------------------------------------------
AC_DEFUN([TEA_TCL_LINK_LIBS], [
#--------------------------------------------------------------------
# On a few very rare systems, all of the libm.a stuff is
# already in libc.a. Set compiler flags accordingly.
- # Also, Linux requires the "ieee" library for math to work
- # right (and it must appear before "-lm").
#--------------------------------------------------------------------
AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm")
- AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
#--------------------------------------------------------------------
# Interactive UNIX requires -linet instead of -lsocket, plus it
@@ -2670,13 +2526,10 @@ AC_DEFUN([TEA_TCL_LINK_LIBS], [
fi
AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname,
[LIBS="$LIBS -lnsl"])])
-
- # TEA specific: Don't perform the eval of the libraries here because
- # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS
-
- TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'
- AC_SUBST(TCL_LIBS)
- AC_SUBST(MATH_LIBS)
+ AC_CHECK_FUNC(mp_log_u32, , [AC_CHECK_LIB(tommath, mp_log_u32,
+ [LIBS="$LIBS -ltommath"])])
+ AC_CHECK_FUNC(deflateSetHeader, , [AC_CHECK_LIB(z, deflateSetHeader,
+ [LIBS="$LIBS -lz"])])
])
#--------------------------------------------------------------------
@@ -2692,19 +2545,19 @@ AC_DEFUN([TEA_TCL_LINK_LIBS], [
#
# Might define the following vars:
# _ISOC99_SOURCE
-# _LARGEFILE64_SOURCE
-# _LARGEFILE_SOURCE64
+# _FILE_OFFSET_BITS
+#
#--------------------------------------------------------------------
AC_DEFUN([TEA_TCL_EARLY_FLAG],[
AC_CACHE_VAL([tcl_cv_flag_]translit($1,[A-Z],[a-z]),
- AC_TRY_COMPILE([$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no,
- AC_TRY_COMPILE([[#define ]$1[ 1
-]$2], $3,
- [tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes,
- [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no)))
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$2]], [[$3]])],
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no,[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[[#define ]$1[ ]m4_default([$4],[1])[
+]$2]], [[$3]])],
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes,
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no)]))
if test ["x${tcl_cv_flag_]translit($1,[A-Z],[a-z])[}" = "xyes"] ; then
- AC_DEFINE($1, 1, [Add the ]$1[ flag when building])
+ AC_DEFINE($1, m4_default([$4],[1]), [Add the ]$1[ flag when building])
tcl_flags="$tcl_flags $1"
fi
])
@@ -2714,10 +2567,10 @@ AC_DEFUN([TEA_TCL_EARLY_FLAGS],[
tcl_flags=""
TEA_TCL_EARLY_FLAG(_ISOC99_SOURCE,[#include ],
[char *p = (char *)strtoll; char *q = (char *)strtoull;])
- TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include ],
- [struct stat64 buf; int i = stat64("/", &buf);])
- TEA_TCL_EARLY_FLAG(_LARGEFILE_SOURCE64,[#include ],
- [char *p = (char *)open64;])
+ if test "${TCL_MAJOR_VERSION}" -ne 8 ; then
+ TEA_TCL_EARLY_FLAG(_FILE_OFFSET_BITS,[#include ],
+ [switch (0) { case 0: case (sizeof(off_t)==sizeof(long long)): ; }],64)
+ fi
if test "x${tcl_flags}" = "x" ; then
AC_MSG_RESULT([none])
else
@@ -2738,9 +2591,11 @@ AC_DEFUN([TEA_TCL_EARLY_FLAGS],[
# Might define the following vars:
# TCL_WIDE_INT_IS_LONG
# TCL_WIDE_INT_TYPE
-# HAVE_STRUCT_DIRENT64
+# HAVE_STRUCT_DIRENT64, HAVE_DIR64
# HAVE_STRUCT_STAT64
# HAVE_TYPE_OFF64_T
+# _TIME_BITS
+#
#--------------------------------------------------------------------
AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
@@ -2748,17 +2603,17 @@ AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
AC_CACHE_VAL(tcl_cv_type_64bit,[
tcl_cv_type_64bit=none
# See if the compiler knows natively about __int64
- AC_TRY_COMPILE(,[__int64 value = (__int64) 0;],
- tcl_type_64bit=__int64, tcl_type_64bit="long long")
- # See if we should use long anyway Note that we substitute in the
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[__int64 value = (__int64) 0;]])],
+ [tcl_type_64bit=__int64],[tcl_type_64bit="long long"])
+ # See if we could use long anyway Note that we substitute in the
# type that is our current guess for a 64-bit type inside this check
# program, so it should be modified only carefully...
- AC_TRY_COMPILE(,[switch (0) {
- case 1: case (sizeof(]${tcl_type_64bit}[)==sizeof(long)): ;
- }],tcl_cv_type_64bit=${tcl_type_64bit})])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[switch (0) {
+ case 1: case (sizeof(${tcl_type_64bit})==sizeof(long)): ;
+ }]])],[tcl_cv_type_64bit=${tcl_type_64bit}],[])])
if test "${tcl_cv_type_64bit}" = none ; then
- AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Are wide integers to be implemented with C 'long's?])
- AC_MSG_RESULT([using long])
+ AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Do 'long' and 'long long' have the same size (64-bit)?])
+ AC_MSG_RESULT([yes])
elif test "${tcl_cv_type_64bit}" = "__int64" \
-a "${TEA_PLATFORM}" = "windows" ; then
# TEA specific: We actually want to use the default tcl.h checks in
@@ -2770,18 +2625,46 @@ AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
AC_MSG_RESULT([${tcl_cv_type_64bit}])
# Now check for auxiliary declarations
+ if test "${TCL_MAJOR_VERSION}" -ne 8 ; then
+ AC_CACHE_CHECK([for 64-bit time_t], tcl_cv_time_t_64,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]],
+ [[switch (0) {case 0: case (sizeof(time_t)==sizeof(long long)): ;}]])],
+ [tcl_cv_time_t_64=yes],[tcl_cv_time_t_64=no])])
+ if test "x${tcl_cv_time_t_64}" = "xno" ; then
+ # Note that _TIME_BITS=64 requires _FILE_OFFSET_BITS=64
+ # which SC_TCL_EARLY_FLAGS has defined if necessary.
+ AC_CACHE_CHECK([if _TIME_BITS=64 enables 64-bit time_t], tcl_cv__time_bits,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _TIME_BITS 64
+#include ]],
+ [[switch (0) {case 0: case (sizeof(time_t)==sizeof(long long)): ;}]])],
+ [tcl_cv__time_bits=yes],[tcl_cv__time_bits=no])])
+ if test "x${tcl_cv__time_bits}" = "xyes" ; then
+ AC_DEFINE(_TIME_BITS, 64, [_TIME_BITS=64 enables 64-bit time_t.])
+ fi
+ fi
+ fi
+
AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[
- AC_TRY_COMPILE([#include
-#include ],[struct dirent64 p;],
- tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include
+#include ]], [[struct dirent64 p;]])],
+ [tcl_cv_struct_dirent64=yes],[tcl_cv_struct_dirent64=no])])
if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then
AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in ?])
fi
+ AC_CACHE_CHECK([for DIR64], tcl_cv_DIR64,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include
+#include ]], [[struct dirent64 *p; DIR64 d = opendir64(".");
+ p = readdir64(d); rewinddir64(d); closedir64(d);]])],
+ [tcl_cv_DIR64=yes], [tcl_cv_DIR64=no])])
+ if test "x${tcl_cv_DIR64}" = "xyes" ; then
+ AC_DEFINE(HAVE_DIR64, 1, [Is 'DIR64' in ?])
+ fi
+
AC_CACHE_CHECK([for struct stat64], tcl_cv_struct_stat64,[
- AC_TRY_COMPILE([#include ],[struct stat64 p;
-],
- tcl_cv_struct_stat64=yes,tcl_cv_struct_stat64=no)])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct stat64 p;
+]])],
+ [tcl_cv_struct_stat64=yes], [tcl_cv_struct_stat64=no])])
if test "x${tcl_cv_struct_stat64}" = "xyes" ; then
AC_DEFINE(HAVE_STRUCT_STAT64, 1, [Is 'struct stat64' in ?])
fi
@@ -2789,14 +2672,14 @@ AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
AC_CHECK_FUNCS(open64 lseek64)
AC_MSG_CHECKING([for off64_t])
AC_CACHE_VAL(tcl_cv_type_off64_t,[
- AC_TRY_COMPILE([#include ],[off64_t offset;
-],
- tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[off64_t offset;
+]])],
+ [tcl_cv_type_off64_t=yes], [tcl_cv_type_off64_t=no])])
dnl Define HAVE_TYPE_OFF64_T only when the off64_t type and the
dnl functions lseek64 and open64 are defined.
if test "x${tcl_cv_type_off64_t}" = "xyes" && \
- test "x${ac_cv_func_lseek64}" = "xyes" && \
- test "x${ac_cv_func_open64}" = "xyes" ; then
+ test "x${ac_cv_func_lseek64}" = "xyes" && \
+ test "x${ac_cv_func_open64}" = "xyes" ; then
AC_DEFINE(HAVE_TYPE_OFF64_T, 1, [Is off64_t in ?])
AC_MSG_RESULT([yes])
else
@@ -2840,23 +2723,14 @@ AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
#------------------------------------------------------------------------
AC_DEFUN([TEA_INIT], [
- # TEA extensions pass this us the version of TEA they think they
- # are compatible with.
- TEA_VERSION="3.9"
+ TEA_VERSION="3.13"
- AC_MSG_CHECKING([for correct TEA configuration])
+ AC_MSG_CHECKING([TEA configuration])
if test x"${PACKAGE_NAME}" = x ; then
AC_MSG_ERROR([
-The PACKAGE_NAME variable must be defined by your TEA configure.in])
- fi
- if test x"$1" = x ; then
- AC_MSG_ERROR([
-TEA version not specified.])
- elif test "$1" != "${TEA_VERSION}" ; then
- AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"])
- else
- AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
+The PACKAGE_NAME variable must be defined by your TEA configure.ac])
fi
+ AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
# If the user did not set CFLAGS, set it now to keep macros
# like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
@@ -2865,15 +2739,14 @@ TEA version not specified.])
fi
case "`uname -s`" in
- *win32*|*WIN32*|*MINGW32_*)
- AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo)
+ *win32*|*WIN32*|*MINGW32_*|*MINGW64_*|*MSYS_*)
+ AC_CHECK_PROG(CYGPATH, cygpath, cygpath -m, echo)
EXEEXT=".exe"
TEA_PLATFORM="windows"
;;
*CYGWIN_*)
- CYGPATH=echo
EXEEXT=".exe"
- # TEA_PLATFORM is determined later in LOAD_TCLCONFIG
+ # CYGPATH and TEA_PLATFORM are determined later in LOAD_TCLCONFIG
;;
*)
CYGPATH=echo
@@ -2907,8 +2780,8 @@ TEA version not specified.])
# This package name must be replaced statically for AC_SUBST to work
AC_SUBST(PKG_LIB_FILE)
- # Substitute STUB_LIB_FILE in case package creates a stub library too.
- AC_SUBST(PKG_STUB_LIB_FILE)
+ AC_SUBST(PKG_LIB_FILE8)
+ AC_SUBST(PKG_LIB_FILE9)
# We AC_SUBST these here to ensure they are subst'ed,
# in case the user doesn't call TEA_ADD_...
@@ -2919,6 +2792,9 @@ TEA version not specified.])
AC_SUBST(PKG_INCLUDES)
AC_SUBST(PKG_LIBS)
AC_SUBST(PKG_CFLAGS)
+
+ # Configure the installer.
+ TEA_INSTALLER
])
#------------------------------------------------------------------------
@@ -3111,7 +2987,7 @@ AC_DEFUN([TEA_ADD_LIBS], [
for i in $vars; do
if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
# Convert foo.lib to -lfoo for GCC. No-op if not *.lib
- i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.lib[$]/-l\1/i'`
+ i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.[[lL]][[iI]][[bB]][$]/-l\1/'`
fi
PKG_LIBS="$PKG_LIBS $i"
done
@@ -3194,7 +3070,7 @@ AC_DEFUN([TEA_PREFIX], [
# TEA_SETUP_COMPILER_CC --
#
# Do compiler checks the way we want. This is just a replacement
-# for AC_PROG_CC in TEA configure.in files to make them cleaner.
+# for AC_PROG_CC in TEA configure.ac files to make them cleaner.
#
# Arguments:
# none
@@ -3210,15 +3086,6 @@ AC_DEFUN([TEA_SETUP_COMPILER_CC], [
AC_PROG_CC
AC_PROG_CPP
- INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c"
- AC_SUBST(INSTALL)
- INSTALL_DATA="\${INSTALL} -m 644"
- AC_SUBST(INSTALL_DATA)
- INSTALL_PROGRAM="\${INSTALL}"
- AC_SUBST(INSTALL_PROGRAM)
- INSTALL_SCRIPT="\${INSTALL}"
- AC_SUBST(INSTALL_SCRIPT)
-
#--------------------------------------------------------------------
# Checks to see if the make program sets the $MAKE variable.
#--------------------------------------------------------------------
@@ -3265,26 +3132,22 @@ AC_DEFUN([TEA_SETUP_COMPILER], [
AC_CACHE_CHECK([if the compiler understands -pipe],
tcl_cv_cc_pipe, [
hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe"
- AC_TRY_COMPILE(,, tcl_cv_cc_pipe=yes, tcl_cv_cc_pipe=no)
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[tcl_cv_cc_pipe=yes],[tcl_cv_cc_pipe=no])
CFLAGS=$hold_cflags])
if test $tcl_cv_cc_pipe = yes; then
CFLAGS="$CFLAGS -pipe"
fi
fi
+ if test "${TCL_MAJOR_VERSION}" -lt 9 -a "${TCL_MINOR_VERSION}" -lt 7; then
+ AC_DEFINE(Tcl_Size, int, [Is 'Tcl_Size' in ?])
+ fi
+
#--------------------------------------------------------------------
# Common compiler flag setup
#--------------------------------------------------------------------
- AC_C_BIGENDIAN
- if test "${TEA_PLATFORM}" = "unix" ; then
- TEA_TCL_LINK_LIBS
- TEA_MISSING_POSIX_HEADERS
- # Let the user call this, because if it triggers, they will
- # need a compat/strtod.c that is correct. Users can also
- # use Tcl_GetDouble(FromObj) instead.
- #TEA_BUGGY_STRTOD
- fi
+ AC_C_BIGENDIAN(,,,[#])
])
#------------------------------------------------------------------------
@@ -3315,7 +3178,7 @@ AC_DEFUN([TEA_SETUP_COMPILER], [
AC_DEFUN([TEA_MAKE_LIB], [
if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then
MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)"
- MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)"
+ MAKE_SHARED_LIB="\${SHLIB_LD} \${LDFLAGS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
AC_EGREP_CPP([manifest needed], [
#if defined(_MSC_VER) && _MSC_VER >= 1400
print("manifest needed")
@@ -3330,7 +3193,7 @@ print("manifest needed")
MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@ \$(PKG_STUB_OBJECTS)"
else
MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)"
- MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
+ MAKE_SHARED_LIB="\${SHLIB_LD} \${LDFLAGS} \${LDFLAGS_DEFAULT} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(PKG_STUB_OBJECTS)"
fi
@@ -3346,6 +3209,14 @@ print("manifest needed")
# substituted. (@@@ Might not be necessary anymore)
#--------------------------------------------------------------------
+ PACKAGE_LIB_PREFIX8="${PACKAGE_LIB_PREFIX}"
+ PACKAGE_LIB_PREFIX9="${PACKAGE_LIB_PREFIX}tcl9"
+ if test "${TCL_MAJOR_VERSION}" -gt 8 -a x"${with_tcl8}" = x; then
+ PACKAGE_LIB_PREFIX="${PACKAGE_LIB_PREFIX9}"
+ else
+ PACKAGE_LIB_PREFIX="${PACKAGE_LIB_PREFIX8}"
+ AC_DEFINE(TCL_MAJOR_VERSION, 8, [Compile for Tcl8?])
+ fi
if test "${TEA_PLATFORM}" = "windows" ; then
if test "${SHARED_BUILD}" = "1" ; then
# We force the unresolved linking of symbols that are really in
@@ -3357,15 +3228,23 @@ print("manifest needed")
if test "$GCC" = "yes"; then
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc"
fi
- eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE8=${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE9=${PACKAGE_LIB_PREFIX9}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE=${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
else
- eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
if test "$GCC" = "yes"; then
- PKG_LIB_FILE=lib${PKG_LIB_FILE}
+ PACKAGE_LIB_PREFIX=lib${PACKAGE_LIB_PREFIX}
fi
+ eval eval "PKG_LIB_FILE8=${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE9=${PACKAGE_LIB_PREFIX9}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE=${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
fi
# Some packages build their own stubs libraries
- eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ if test "${TCL_MAJOR_VERSION}" -gt 8 -a x"${with_tcl8}" = x; then
+ eval eval "PKG_STUB_LIB_FILE=${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}stub.a"
+ else
+ eval eval "PKG_STUB_LIB_FILE=${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ fi
if test "$GCC" = "yes"; then
PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE}
fi
@@ -3379,13 +3258,21 @@ print("manifest needed")
if test x"${TK_BIN_DIR}" != x ; then
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
fi
- eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE8=lib${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE9=lib${PACKAGE_LIB_PREFIX9}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
RANLIB=:
else
- eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE8=lib${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE9=lib${PACKAGE_LIB_PREFIX9}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_LIB_PREFIX}${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
fi
# Some packages build their own stubs libraries
- eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ if test "${TCL_MAJOR_VERSION}" -gt 8 -a x"${with_tcl8}" = x; then
+ eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}stub.a"
+ else
+ eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_LIB_PREFIX8}${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ fi
fi
# These are escaped so that only CFLAGS is picked up at configure time.
@@ -3399,6 +3286,8 @@ print("manifest needed")
AC_SUBST(MAKE_SHARED_LIB)
AC_SUBST(MAKE_STATIC_LIB)
AC_SUBST(MAKE_STUB_LIB)
+ # Substitute STUB_LIB_FILE in case package creates a stub library too.
+ AC_SUBST(PKG_STUB_LIB_FILE)
AC_SUBST(RANLIB_STUB)
AC_SUBST(VC_MANIFEST_EMBED_DLL)
AC_SUBST(VC_MANIFEST_EMBED_EXE)
@@ -3525,9 +3414,9 @@ AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [
# any *_NATIVE vars be defined in the Makefile
TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}"
if test "`uname -s`" = "Darwin"; then
- # If Tcl was built as a framework, attempt to use
- # the framework's Headers and PrivateHeaders directories
- case ${TCL_DEFS} in
+ # If Tcl was built as a framework, attempt to use
+ # the framework's Headers and PrivateHeaders directories
+ case ${TCL_DEFS} in
*TCL_FRAMEWORK*)
if test -d "${TCL_BIN_DIR}/Headers" -a \
-d "${TCL_BIN_DIR}/PrivateHeaders"; then
@@ -3535,7 +3424,7 @@ AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [
else
TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`"
fi
- ;;
+ ;;
esac
result="Using ${TCL_INCLUDES}"
else
@@ -3930,6 +3819,7 @@ AC_DEFUN([TEA_PATH_CONFIG], [
`ls -d ${prefix}/lib 2>/dev/null` \
`ls -d /usr/local/lib 2>/dev/null` \
`ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/pkg/lib 2>/dev/null` \
`ls -d /usr/lib 2>/dev/null` \
`ls -d /usr/lib64 2>/dev/null` \
; do
@@ -3975,10 +3865,10 @@ AC_DEFUN([TEA_LOAD_CONFIG], [
AC_MSG_CHECKING([for existence of ${$1_BIN_DIR}/$1Config.sh])
if test -f "${$1_BIN_DIR}/$1Config.sh" ; then
- AC_MSG_RESULT([loading])
+ AC_MSG_RESULT([loading])
. "${$1_BIN_DIR}/$1Config.sh"
else
- AC_MSG_RESULT([file not found])
+ AC_MSG_RESULT([file not found])
fi
#
@@ -3992,11 +3882,11 @@ AC_DEFUN([TEA_LOAD_CONFIG], [
if test -f "${$1_BIN_DIR}/Makefile" ; then
AC_MSG_WARN([Found Makefile - using build library specs for $1])
- $1_LIB_SPEC=${$1_BUILD_LIB_SPEC}
- $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC}
- $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH}
- $1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC}
- $1_LIBRARY_PATH=${$1_LIBRARY_PATH}
+ $1_LIB_SPEC=${$1_BUILD_LIB_SPEC}
+ $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC}
+ $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH}
+ $1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC}
+ $1_LIBRARY_PATH=${$1_LIBRARY_PATH}
fi
AC_SUBST($1_VERSION)
@@ -4071,18 +3961,22 @@ AC_DEFUN([TEA_EXPORT_CONFIG], [
# pkglibdir must be a fully qualified path and (not ${exec_prefix}/lib)
eval pkglibdir="[$]{libdir}/$1${PACKAGE_VERSION}"
if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
- eval $1_LIB_FLAG="-l$1${PACKAGE_VERSION}${DBGX}"
- eval $1_STUB_LIB_FLAG="-l$1stub${PACKAGE_VERSION}${DBGX}"
+ eval $1_LIB_FLAG="-l$1${PACKAGE_VERSION}"
+ eval $1_STUB_LIB_FLAG="-l$1stub${PACKAGE_VERSION}"
else
- eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
- eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
+ eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`"
+ eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`"
fi
- $1_BUILD_LIB_SPEC="-L`pwd` ${$1_LIB_FLAG}"
- $1_LIB_SPEC="-L${pkglibdir} ${$1_LIB_FLAG}"
- $1_BUILD_STUB_LIB_SPEC="-L`pwd` [$]{$1_STUB_LIB_FLAG}"
- $1_STUB_LIB_SPEC="-L${pkglibdir} [$]{$1_STUB_LIB_FLAG}"
- $1_BUILD_STUB_LIB_PATH="`pwd`/[$]{PKG_STUB_LIB_FILE}"
- $1_STUB_LIB_PATH="${pkglibdir}/[$]{PKG_STUB_LIB_FILE}"
+ if test "${TCL_MAJOR_VERSION}" -gt 8 -a x"${with_tcl8}" = x; then
+ eval $1_STUB_LIB_FLAG="-l$1stub"
+ fi
+
+ $1_BUILD_LIB_SPEC="-L`$CYGPATH $(pwd)` ${$1_LIB_FLAG}"
+ $1_LIB_SPEC="-L`$CYGPATH ${pkglibdir}` ${$1_LIB_FLAG}"
+ $1_BUILD_STUB_LIB_SPEC="-L`$CYGPATH $(pwd)` [$]{$1_STUB_LIB_FLAG}"
+ $1_STUB_LIB_SPEC="-L`$CYGPATH ${pkglibdir}` [$]{$1_STUB_LIB_FLAG}"
+ $1_BUILD_STUB_LIB_PATH="`$CYGPATH $(pwd)`/[$]{PKG_STUB_LIB_FILE}"
+ $1_STUB_LIB_PATH="`$CYGPATH ${pkglibdir}`/[$]{PKG_STUB_LIB_FILE}"
AC_SUBST($1_BUILD_LIB_SPEC)
AC_SUBST($1_LIB_SPEC)
@@ -4098,71 +3992,128 @@ AC_DEFUN([TEA_EXPORT_CONFIG], [
#------------------------------------------------------------------------
-# TEA_PATH_CELIB --
+# TEA_INSTALLER --
#
-# Locate Keuchel's celib emulation layer for targeting Win/CE
+# Configure the installer.
#
# Arguments:
# none
#
# Results:
+# Substitutes the following vars:
+# INSTALL
+# INSTALL_DATA_DIR
+# INSTALL_DATA
+# INSTALL_PROGRAM
+# INSTALL_SCRIPT
+# INSTALL_LIBRARY
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_INSTALLER], [
+ INSTALL='$(SHELL) $(srcdir)/tclconfig/install-sh -c'
+ INSTALL_DATA_DIR='${INSTALL} -d -m 755'
+ INSTALL_DATA='${INSTALL} -m 644'
+ INSTALL_PROGRAM='${INSTALL} -m 755'
+ INSTALL_SCRIPT='${INSTALL} -m 755'
+
+ TEA_CONFIG_SYSTEM
+ case $system in
+ HP-UX-*) INSTALL_LIBRARY='${INSTALL} -m 755' ;;
+ *) INSTALL_LIBRARY='${INSTALL} -m 644' ;;
+ esac
+
+ AC_SUBST(INSTALL)
+ AC_SUBST(INSTALL_DATA_DIR)
+ AC_SUBST(INSTALL_DATA)
+ AC_SUBST(INSTALL_PROGRAM)
+ AC_SUBST(INSTALL_SCRIPT)
+ AC_SUBST(INSTALL_LIBRARY)
+])
+
+###
+# Tip 430 - ZipFS Modifications
+###
+#------------------------------------------------------------------------
+# TEA_ZIPFS_SUPPORT
+# Locate a zip encoder installed on the system path, or none.
#
-# Adds the following arguments to configure:
-# --with-celib=...
+# Arguments:
+# none
#
-# Defines the following vars:
-# CELIB_DIR Full path to the directory containing
-# the include and platform lib files
+# Results:
+# Substitutes the following vars:
+# MACHER_PROG
+# ZIP_PROG
+# ZIP_PROG_OPTIONS
+# ZIP_PROG_VFSSEARCH
+# ZIP_INSTALL_OBJS
#------------------------------------------------------------------------
-AC_DEFUN([TEA_PATH_CELIB], [
- # First, look for one uninstalled.
- # the alternative search directory is invoked by --with-celib
-
- if test x"${no_celib}" = x ; then
- # we reset no_celib in case something fails here
- no_celib=true
- AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], with_celibconfig=${withval})
- AC_MSG_CHECKING([for Windows/CE celib directory])
- AC_CACHE_VAL(ac_cv_c_celibconfig,[
- # First check to see if --with-celibconfig was specified.
- if test x"${with_celibconfig}" != x ; then
- if test -d "${with_celibconfig}/inc" ; then
- ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)`
- else
- AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory])
- fi
+AC_DEFUN([TEA_ZIPFS_SUPPORT], [
+ MACHER_PROG=""
+ ZIP_PROG=""
+ ZIP_PROG_OPTIONS=""
+ ZIP_PROG_VFSSEARCH=""
+ ZIP_INSTALL_OBJS=""
+
+ AC_MSG_CHECKING([for macher])
+ AC_CACHE_VAL(ac_cv_path_macher, [
+ search_path=`echo ${PATH} | sed -e 's/:/ /g'`
+ for dir in $search_path ; do
+ for j in `ls -r $dir/macher 2> /dev/null` \
+ `ls -r $dir/macher 2> /dev/null` ; do
+ if test x"$ac_cv_path_macher" = x ; then
+ if test -f "$j" ; then
+ ac_cv_path_macher=$j
+ break
fi
-
- # then check for a celib library
- if test x"${ac_cv_c_celibconfig}" = x ; then
- for i in \
- ../celib-palm-3.0 \
- ../celib \
- ../../celib-palm-3.0 \
- ../../celib \
- `ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \
- ${srcdir}/../celib-palm-3.0 \
- ${srcdir}/../celib \
- `ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \
- ; do
- if test -d "$i/inc" ; then
- ac_cv_c_celibconfig=`(cd $i; pwd)`
- break
- fi
- done
+ fi
+ done
+ done
+ ])
+ if test -f "$ac_cv_path_macher" ; then
+ MACHER_PROG="$ac_cv_path_macher"
+ AC_MSG_RESULT([$MACHER_PROG])
+ AC_MSG_RESULT([Found macher in environment])
+ fi
+ AC_MSG_CHECKING([for zip])
+ AC_CACHE_VAL(ac_cv_path_zip, [
+ search_path=`echo ${PATH} | sed -e 's/:/ /g'`
+ for dir in $search_path ; do
+ for j in `ls -r $dir/zip 2> /dev/null` \
+ `ls -r $dir/zip 2> /dev/null` ; do
+ if test x"$ac_cv_path_zip" = x ; then
+ if test -f "$j" ; then
+ ac_cv_path_zip=$j
+ break
fi
- ])
- if test x"${ac_cv_c_celibconfig}" = x ; then
- AC_MSG_ERROR([Cannot find celib support library directory])
- else
- no_celib=
- CELIB_DIR=${ac_cv_c_celibconfig}
- CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'`
- AC_MSG_RESULT([found $CELIB_DIR])
fi
+ done
+ done
+ ])
+ if test -f "$ac_cv_path_zip" ; then
+ ZIP_PROG="$ac_cv_path_zip"
+ AC_MSG_RESULT([$ZIP_PROG])
+ ZIP_PROG_OPTIONS="-rq"
+ ZIP_PROG_VFSSEARCH="*"
+ AC_MSG_RESULT([Found INFO Zip in environment])
+ # Use standard arguments for zip
+ else
+ # It is not an error if an installed version of Zip can't be located.
+ # We can use the locally distributed minizip instead
+ ZIP_PROG="./minizip${EXEEXT_FOR_BUILD}"
+ ZIP_PROG_OPTIONS="-o -r"
+ ZIP_PROG_VFSSEARCH="*"
+ ZIP_INSTALL_OBJS="minizip${EXEEXT_FOR_BUILD}"
+ AC_MSG_RESULT([No zip found on PATH. Building minizip])
fi
+ AC_SUBST(MACHER_PROG)
+ AC_SUBST(ZIP_PROG)
+ AC_SUBST(ZIP_PROG_OPTIONS)
+ AC_SUBST(ZIP_PROG_VFSSEARCH)
+ AC_SUBST(ZIP_INSTALL_OBJS)
])
+
# Local Variables:
# mode: autoconf
# End:
diff --git a/sqlite/autoconf/tea/win/makefile.vc b/sqlite/autoconf/tea/win/makefile.vc
index a5e46277..bb32f1a7 100644
--- a/sqlite/autoconf/tea/win/makefile.vc
+++ b/sqlite/autoconf/tea/win/makefile.vc
@@ -1,414 +1,61 @@
-# makefile.vc -- -*- Makefile -*-
+#------------------------------------------------------------- -*- makefile -*-
#
-# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)
+# Sample makefile for building Tcl extensions.
#
-# This makefile is based upon the Tcl 8.4 Makefile.vc and modified to
-# make it suitable as a general package makefile. Look for the word EDIT
-# which marks sections that may need modification. As a minumum you will
-# need to change the PROJECT, DOTVERSION and DLLOBJS variables to values
-# relevant to your package.
+# Basic build, test and install
+# nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl
+# nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl test
+# nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl install
+#
+# For other build options (debug, static etc.)
+# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
+# detailed documentation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
-#
-# Copyright (c) 1995-1996 Sun Microsystems, Inc.
-# Copyright (c) 1998-2000 Ajuba Solutions.
-# Copyright (c) 2001 ActiveState Corporation.
-# Copyright (c) 2001-2002 David Gravereaux.
-# Copyright (c) 2003 Pat Thoyts
#
-#-------------------------------------------------------------------------
-# RCS: @(#)$Id: makefile.vc,v 1.4 2004/07/26 08:22:05 patthoyts Exp $
-#-------------------------------------------------------------------------
-
-!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
-MSG = ^
-You will need to run vcvars32.bat from Developer Studio, first, to setup^
-the environment. Jump to this line to read the new instructions.
-!error $(MSG)
-!endif
-
#------------------------------------------------------------------------------
-# HOW TO USE this makefile:
-#
-# 1) It is now necessary to have %MSVCDir% set in the environment. This is
-# used as a check to see if vcvars32.bat had been run prior to running
-# nmake or during the installation of Microsoft Visual C++, MSVCDir had
-# been set globally and the PATH adjusted. Either way is valid.
-#
-# You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin
-# directory to setup the proper environment, if needed, for your current
-# setup. This is a needed bootstrap requirement and allows the swapping of
-# different environments to be easier.
-#
-# 2) To use the Platform SDK (not expressly needed), run setenv.bat after
-# vcvars32.bat according to the instructions for it. This can also turn on
-# the 64-bit compiler, if your SDK has it.
-#
-# 3) Targets are:
-# all -- Builds everything.
-# -- Builds the project (eg: nmake sample)
-# test -- Builds and runs the test suite.
-# install -- Installs the built binaries and libraries to $(INSTALLDIR)
-# in an appropriate subdirectory.
-# clean/realclean/distclean -- varying levels of cleaning.
-#
-# 4) Macros usable on the commandline:
-# INSTALLDIR=
-# Sets where to install Tcl from the built binaries.
-# C:\Progra~1\Tcl is assumed when not specified.
-#
-# OPTS=static,msvcrt,staticpkg,threads,symbols,profile,loimpact,none
-# Sets special options for the core. The default is for none.
-# Any combination of the above may be used (comma separated).
-# 'none' will over-ride everything to nothing.
-#
-# static = Builds a static library of the core instead of a
-# dll. The shell will be static (and large), as well.
-# msvcrt = Effects the static option only to switch it from
-# using libcmt(d) as the C runtime [by default] to
-# msvcrt(d). This is useful for static embedding
-# support.
-# staticpkg = Effects the static option only to switch
-# tclshXX.exe to have the dde and reg extension linked
-# inside it.
-# threads = Turns on full multithreading support.
-# thrdalloc = Use the thread allocator (shared global free pool).
-# symbols = Adds symbols for step debugging.
-# profile = Adds profiling hooks. Map file is assumed.
-# loimpact = Adds a flag for how NT treats the heap to keep memory
-# in use, low. This is said to impact alloc performance.
-#
-# STATS=memdbg,compdbg,none
-# Sets optional memory and bytecode compiler debugging code added
-# to the core. The default is for none. Any combination of the
-# above may be used (comma separated). 'none' will over-ride
-# everything to nothing.
-#
-# memdbg = Enables the debugging memory allocator.
-# compdbg = Enables byte compilation logging.
-#
-# MACHINE=(IX86|IA64|ALPHA)
-# Set the machine type used for the compiler, linker, and
-# resource compiler. This hook is needed to tell the tools
-# when alternate platforms are requested. IX86 is the default
-# when not specified.
-#
-# TMP_DIR=
-# OUT_DIR=
-# Hooks to allow the intermediate and output directories to be
-# changed. $(OUT_DIR) is assumed to be
-# $(BINROOT)\(Release|Debug) based on if symbols are requested.
-# $(TMP_DIR) will de $(OUT_DIR)\ by default.
-#
-# TESTPAT=
-# Reads the tests requested to be run from this file.
-#
-# CFG_ENCODING=encoding
-# name of encoding for configuration information. Defaults
-# to cp1252
-#
-# 5) Examples:
-#
-# Basic syntax of calling nmake looks like this:
-# nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]]
-#
-# Standard (no frills)
-# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
-# Setting environment for using Microsoft Visual C++ tools.
-# c:\tcl_src\win\>nmake -f makefile.vc all
-# c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl
-#
-# Building for Win64
-# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
-# Setting environment for using Microsoft Visual C++ tools.
-# c:\tcl_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL
-# Targeting Windows pre64 RETAIL
-# c:\tcl_src\win\>nmake -f makefile.vc MACHINE=IA64
-#
-#------------------------------------------------------------------------------
-#==============================================================================
-###############################################################################
-#------------------------------------------------------------------------------
-
-!if !exist("makefile.vc")
-MSG = ^
-You must run this makefile only from the directory it is in.^
-Please `cd` to its location first.
-!error $(MSG)
-!endif
-
-#-------------------------------------------------------------------------
-# Project specific information (EDIT)
-#
-# You should edit this with the name and version of your project. This
-# information is used to generate the name of the package library and
-# it's install location.
-#
-# For example, the sample extension is going to build sample04.dll and
-# would install it into $(INSTALLDIR)\lib\sample04
-#
-# You need to specify the object files that need to be linked into your
-# binary here.
-#
-#-------------------------------------------------------------------------
-
-PROJECT = sqlite3
-!include "rules.vc"
-
-# nmakehelp -V will search the file for tag, skips until a
-# number and returns all character until a character not in [0-9.ab]
-# is read.
-
-!if [echo REM = This file is generated from Makefile.vc > versions.vc]
-!endif
-# get project version from row "AC_INIT([sqlite], [3.7.14])"
-!if [echo DOTVERSION = \>> versions.vc] \
- && [nmakehlp -V ..\configure.in AC_INIT >> versions.vc]
-!endif
-!include "versions.vc"
-
-VERSION = $(DOTVERSION:.=)
-STUBPREFIX = $(PROJECT)stub
-
-DLLOBJS = \
- $(TMP_DIR)\tclsqlite3.obj
-
-#-------------------------------------------------------------------------
-# Target names and paths ( shouldn't need changing )
-#-------------------------------------------------------------------------
-
-BINROOT = .
-ROOT = ..
-
-PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
-PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
-PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
-PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
-PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME)
+# PROJECT is sqlite, not sqlite3 to match TEA AC_INIT definition.
+# This makes the generated DLL name also consistent between the two
+# except for the "t" suffix which is the convention for nmake builds.
+PROJECT = sqlite
+PRJ_PACKAGE_TCLNAME = sqlite3
-### Make sure we use backslash only.
-PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
-LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR)
-BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR)
-DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR)
-SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR)
-INCLUDE_INSTALL_DIR = $(_TCLDIR)\include
+!include "rules-ext.vc"
-### The following paths CANNOT have spaces in them.
-GENERICDIR = $(ROOT)\generic
-WINDIR = $(ROOT)\win
-LIBDIR = $(ROOT)\library
-DOCDIR = $(ROOT)\doc
-TOOLSDIR = $(ROOT)\tools
-COMPATDIR = $(ROOT)\compat
+PRJ_OBJS = $(TMP_DIR)\tclsqlite3.obj
-#---------------------------------------------------------------------
-# Compile flags
-#---------------------------------------------------------------------
-
-!if !$(DEBUG)
-!if $(OPTIMIZING)
-### This cranks the optimization level to maximize speed
-cdebug = -O2 -Op -Gs
-!else
-cdebug =
-!endif
-!else if "$(MACHINE)" == "IA64"
-### Warnings are too many, can't support warnings into errors.
-cdebug = -Z7 -Od -GZ
-!else
-cdebug = -Z7 -WX -Od -GZ
-!endif
-
-### Declarations common to all compiler options
-cflags = -nologo -c -W3 -YX -Fp$(TMP_DIR)^\
-
-!if $(MSVCRT)
-!if $(DEBUG)
-crt = -MDd
-!else
-crt = -MD
-!endif
-!else
-!if $(DEBUG)
-crt = -MTd
-!else
-crt = -MT
-!endif
-!endif
-
-INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" \
- -I"$(ROOT)\.."
-BASE_CLFAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) \
+# Preprocessor macros specific to sqlite3.
+PRJ_DEFINES = -I"$(ROOT)\.." -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE \
+ -DSQLITE_ENABLE_DBPAGE_VTAB=1 -DSQLITE_ENABLE_DBSTAT_VTAB=1 \
+ -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS4=1 \
+ -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 \
+ -DSQLITE_ENABLE_JSON1=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 \
-DSQLITE_3_SUFFIX_ONLY=1 -DSQLITE_ENABLE_RTREE=1 \
- -DSQLITE_ENABLE_FTS3=1 -DSQLITE_OMIT_DEPRECATED=1
-CON_CFLAGS = $(cflags) $(cdebug) $(crt) -DCONSOLE -DSQLITE_ENABLE_FTS3=1
-TCL_CFLAGS = -DBUILD_sqlite -DUSE_TCL_STUBS \
- -DPACKAGE_VERSION="\"$(DOTVERSION)\"" $(BASE_CLFAGS) \
- $(OPTDEFINES)
-
-#---------------------------------------------------------------------
-# Link flags
-#---------------------------------------------------------------------
-
-!if $(DEBUG)
-ldebug = -debug:full -debugtype:cv
-!else
-ldebug = -release -opt:ref -opt:icf,3
-!endif
-
-### Declarations common to all linker options
-lflags = -nologo -machine:$(MACHINE) $(ldebug)
-
-!if $(PROFILE)
-lflags = $(lflags) -profile
-!endif
-
-!if $(ALIGN98_HACK) && !$(STATIC_BUILD)
-### Align sections for PE size savings.
-lflags = $(lflags) -opt:nowin98
-!else if !$(ALIGN98_HACK) && $(STATIC_BUILD)
-### Align sections for speed in loading by choosing the virtual page size.
-lflags = $(lflags) -align:4096
-!endif
-
-!if $(LOIMPACT)
-lflags = $(lflags) -ws:aggressive
-!endif
-
-dlllflags = $(lflags) -dll
-conlflags = $(lflags) -subsystem:console
-guilflags = $(lflags) -subsystem:windows
-baselibs = $(TCLSTUBLIB)
-
-#---------------------------------------------------------------------
-# TclTest flags
-#---------------------------------------------------------------------
-
-!IF "$(TESTPAT)" != ""
-TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
-!ENDIF
-
-#---------------------------------------------------------------------
-# Project specific targets (EDIT)
-#---------------------------------------------------------------------
-
-all: setup $(PROJECT)
-$(PROJECT): setup $(PRJLIB)
-install: install-binaries install-libraries install-docs
-
-# Tests need to ensure we load the right dll file we
-# have to handle the output differently on Win9x.
-#
-!if "$(OS)" == "Windows_NT" || "$(MSVCDIR)" == "IDE"
-test: setup $(PROJECT)
- set TCL_LIBRARY=$(ROOT)/library
- $(TCLSH) <<
-load $(PRJLIB:\=/)
-cd "$(ROOT)/tests"
-set argv "$(TESTFLAGS)"
-source all.tcl
-<<
-!else
-test: setup $(PROJECT)
- echo Please wait while the test results are collected
- set TCL_LIBRARY=$(ROOT)/library
- $(TCLSH) << >tests.log
-load $(PRJLIB:\=/)
-cd "$(ROOT)/tests"
-set argv "$(TESTFLAGS)"
-source all.tcl
-<<
- type tests.log | more
-!endif
-
-setup:
- @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
- @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
-
-$(PRJLIB): $(DLLOBJS)
- $(link32) $(dlllflags) -out:$@ $(baselibs) @<<
-$**
-<<
- -@del $*.exp
-
-$(PRJSTUBLIB): $(PRJSTUBOBJS)
- $(lib32) -nologo -out:$@ $(PRJSTUBOBJS)
-
-#---------------------------------------------------------------------
-# Implicit rules
-#---------------------------------------------------------------------
-
-{$(WINDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
-$<
-<<
-
-{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
-$<
-<<
-
-{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
- $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
-$<
-<<
-
-{$(WINDIR)}.rc{$(TMP_DIR)}.res:
- $(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \
-!if $(DEBUG)
- -d DEBUG \
-!endif
-!if $(TCL_THREADS)
- -d TCL_THREADS \
-!endif
-!if $(STATIC_BUILD)
- -d STATIC_BUILD \
-!endif
- $<
-
-.SUFFIXES:
-.SUFFIXES:.c .rc
-
-#---------------------------------------------------------------------
-# Installation. (EDIT)
-#
-# You may need to modify this section to reflect the final distribution
-# of your files and possibly to generate documentation.
-#
-#---------------------------------------------------------------------
-
-install-binaries:
- @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
- @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
- @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
-
-install-libraries:
- @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
- @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
- @echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
- @type << >"$(SCRIPT_INSTALL_DIR)\pkgIndex.tcl"
-package ifneeded $(PROJECT) $(DOTVERSION) \
- [list load [file join $$dir $(PRJLIBNAME)] sqlite3]
-<<
-
-install-docs:
- @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
- @if exist $(DOCDIR) $(CPY) $(DOCDIR)\*.n "$(DOC_INSTALL_DIR)"
-
-#---------------------------------------------------------------------
-# Clean up
-#---------------------------------------------------------------------
-
-clean:
- @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
- @if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc
-
-realclean: clean
- @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
-
-distclean: realclean
- @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
- @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
+ -DSQLITE_UNTESTABLE=1 -DSQLITE_OMIT_LOOKASIDE=1 \
+ -DSQLITE_SECURE_DELETE=1 -DSQLITE_SOUNDEX=1 -DSQLITE_ENABLE_GEOPOLY=1 \
+ -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 \
+ -DSQLITE_ENABLE_MATH_FUNCTIONS=1 -DDSQLITE_USE_ALLOCA=1 \
+ -DSQLITE_ENABLE_STAT4=1 -DSQLITE_OMIT_DEPRECATED=1 \
+ -DSQLITE_WIN32_GETVERSIONEX=0 -DSQLITE_WIN32_NO_ANSI=1
+PRJ_DEFINES = $(PRJ_DEFINES) -I$(TMP_DIR)
+
+# Standard targets to build, install, test etc.
+!include "$(_RULESDIR)\targets.vc"
+
+# The built-in pkgindex does no suffice for our extension as
+# the PROJECT name (sqlite) is not same as init function name (Sqlite3)
+pkgindex:
+ @echo if {[package vsatisfies [package provide Tcl] 9.0-]} { > $(OUT_DIR)\pkgIndex.tcl
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME9)] [string totitle $(PRJ_PACKAGE_TCLNAME)]] >> $(OUT_DIR)\pkgIndex.tcl
+ @echo } else { >> $(OUT_DIR)\pkgIndex.tcl
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME8)] [string totitle $(PRJ_PACKAGE_TCLNAME)]] >> $(OUT_DIR)\pkgIndex.tcl
+ @echo } >> $(OUT_DIR)\pkgIndex.tcl
+
+# Install the manpage though on Windows, doubt it does much good
+install: default-install-docs-n
+
+# Explicit dependency rules
diff --git a/sqlite/autoconf/tea/win/nmakehlp.c b/sqlite/autoconf/tea/win/nmakehlp.c
index e00f1b49..2dc33cc6 100644
--- a/sqlite/autoconf/tea/win/nmakehlp.c
+++ b/sqlite/autoconf/tea/win/nmakehlp.c
@@ -14,13 +14,10 @@
#define _CRT_SECURE_NO_DEPRECATE
#include
-#define NO_SHLWAPI_GDI
-#define NO_SHLWAPI_STREAM
-#define NO_SHLWAPI_REG
-#include
+#ifdef _MSC_VER
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "kernel32.lib")
-#pragma comment (lib, "shlwapi.lib")
+#endif
#include
#include
@@ -39,15 +36,15 @@
#endif
-
/* protos */
static int CheckForCompilerFeature(const char *option);
-static int CheckForLinkerFeature(const char *option);
+static int CheckForLinkerFeature(char **options, int count);
static int IsIn(const char *string, const char *substring);
static int SubstituteFile(const char *substs, const char *filename);
static int QualifyPath(const char *path);
-static const char *GetVersionFromFile(const char *filename, const char *match);
+static int LocateDependency(const char *keyfile);
+static const char *GetVersionFromFile(const char *filename, const char *match, int numdots);
static DWORD WINAPI ReadFromPipe(LPVOID args);
/* globals */
@@ -59,8 +56,8 @@ typedef struct {
char buffer[STATICBUFFERSIZE];
} pipeinfo;
-pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
-pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
+pipeinfo Out = {INVALID_HANDLE_VALUE, ""};
+pipeinfo Err = {INVALID_HANDLE_VALUE, ""};
/*
* exitcodes: 0 == no, 1 == yes, 2 == error
@@ -74,6 +71,7 @@ main(
char msg[300];
DWORD dwWritten;
int chars;
+ const char *s;
/*
* Make sure children (cl.exe and link.exe) are kept quiet.
@@ -102,16 +100,16 @@ main(
}
return CheckForCompilerFeature(argv[2]);
case 'l':
- if (argc != 3) {
+ if (argc < 3) {
chars = snprintf(msg, sizeof(msg) - 1,
- "usage: %s -l \n"
+ "usage: %s -l ? ...?\n"
"Tests for whether link.exe supports an option\n"
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
&dwWritten, NULL);
return 2;
}
- return CheckForLinkerFeature(argv[2]);
+ return CheckForLinkerFeature(&argv[2], argc-2);
case 'f':
if (argc == 2) {
chars = snprintf(msg, sizeof(msg) - 1,
@@ -153,8 +151,13 @@ main(
&dwWritten, NULL);
return 0;
}
- printf("%s\n", GetVersionFromFile(argv[2], argv[3]));
- return 0;
+ s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0');
+ if (s && *s) {
+ printf("%s\n", s);
+ return 0;
+ } else
+ return 1; /* Version not found. Return non-0 exit code */
+
case 'Q':
if (argc != 3) {
chars = snprintf(msg, sizeof(msg) - 1,
@@ -166,6 +169,18 @@ main(
return 2;
}
return QualifyPath(argv[2]);
+
+ case 'L':
+ if (argc != 3) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -L keypath\n"
+ "Emit the fully qualified path of directory containing keypath\n"
+ "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ }
+ return LocateDependency(argv[2]);
}
}
chars = snprintf(msg, sizeof(msg) - 1,
@@ -260,7 +275,7 @@ CheckForCompilerFeature(
"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
- FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
+ FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPSTR)&msg[chars],
(300-chars), 0);
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
return 2;
@@ -313,7 +328,8 @@ CheckForCompilerFeature(
static int
CheckForLinkerFeature(
- const char *option)
+ char **options,
+ int count)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
@@ -322,7 +338,8 @@ CheckForLinkerFeature(
char msg[300];
BOOL ok;
HANDLE hProcess, h, pipeThreads[2];
- char cmdline[100];
+ int i;
+ char cmdline[255];
hProcess = GetCurrentProcess();
@@ -368,7 +385,11 @@ CheckForLinkerFeature(
* Append our option for testing.
*/
- lstrcat(cmdline, option);
+ for (i = 0; i < count; i++) {
+ lstrcat(cmdline, " \"");
+ lstrcat(cmdline, options[i]);
+ lstrcat(cmdline, "\"");
+ }
ok = CreateProcess(
NULL, /* Module name. */
@@ -388,7 +409,7 @@ CheckForLinkerFeature(
"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
- FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
+ FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPSTR)&msg[chars],
(300-chars), 0);
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
return 2;
@@ -433,7 +454,9 @@ CheckForLinkerFeature(
return !(strstr(Out.buffer, "LNK1117") != NULL ||
strstr(Err.buffer, "LNK1117") != NULL ||
strstr(Out.buffer, "LNK4044") != NULL ||
- strstr(Err.buffer, "LNK4044") != NULL);
+ strstr(Err.buffer, "LNK4044") != NULL ||
+ strstr(Out.buffer, "LNK4224") != NULL ||
+ strstr(Err.buffer, "LNK4224") != NULL);
}
static DWORD WINAPI
@@ -479,9 +502,9 @@ IsIn(
static const char *
GetVersionFromFile(
const char *filename,
- const char *match)
+ const char *match,
+ int numdots)
{
- size_t cbBuffer = 100;
static char szBuffer[100];
char *szResult = NULL;
FILE *fp = fopen(filename, "rt");
@@ -491,16 +514,17 @@ GetVersionFromFile(
* Read data until we see our match string.
*/
- while (fgets(szBuffer, cbBuffer, fp) != NULL) {
+ while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) {
LPSTR p, q;
p = strstr(szBuffer, match);
if (p != NULL) {
/*
- * Skip to first digit.
+ * Skip to first digit after the match.
*/
- while (*p && !isdigit(*p)) {
+ p += strlen(match);
+ while (*p && !isdigit((unsigned char)*p)) {
++p;
}
@@ -509,13 +533,13 @@ GetVersionFromFile(
*/
q = p;
- while (*q && (isalnum(*q) || *q == '.')) {
+ while (*q && (strchr("0123456789.ab", *q)) && (((!strchr(".ab", *q)
+ && !strchr("ab", q[-1])) || --numdots))) {
++q;
}
- memcpy(szBuffer, p, q - p);
- szBuffer[q-p] = 0;
- szResult = szBuffer;
+ *q = 0;
+ szResult = p;
break;
}
}
@@ -538,7 +562,7 @@ typedef struct list_item_t {
static list_item_t *
list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
{
- list_item_t *itemPtr = malloc(sizeof(list_item_t));
+ list_item_t *itemPtr = (list_item_t *)malloc(sizeof(list_item_t));
if (itemPtr) {
itemPtr->key = strdup(key);
itemPtr->value = strdup(value);
@@ -587,9 +611,7 @@ SubstituteFile(
const char *substitutions,
const char *filename)
{
- size_t cbBuffer = 1024;
static char szBuffer[1024], szCopy[1024];
- char *szResult = NULL;
list_item_t *substPtr = NULL;
FILE *fp, *sp;
@@ -602,7 +624,7 @@ SubstituteFile(
sp = fopen(substitutions, "rt");
if (sp != NULL) {
- while (fgets(szBuffer, cbBuffer, sp) != NULL) {
+ while (fgets(szBuffer, sizeof(szBuffer), sp) != NULL) {
unsigned char *ks, *ke, *vs, *ve;
ks = (unsigned char*)szBuffer;
while (ks && *ks && isspace(*ks)) ++ks;
@@ -619,7 +641,7 @@ SubstituteFile(
}
/* debug: dump the list */
-#ifdef _DEBUG
+#ifndef NDEBUG
{
int n = 0;
list_item_t *p = NULL;
@@ -628,12 +650,12 @@ SubstituteFile(
}
}
#endif
-
+
/*
* Run the substitutions over each line of the input
*/
-
- while (fgets(szBuffer, cbBuffer, fp) != NULL) {
+
+ while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) {
list_item_t *p = NULL;
for (p = substPtr; p != NULL; p = p->nextPtr) {
char *m = strstr(szBuffer, p->key);
@@ -650,15 +672,26 @@ SubstituteFile(
memcpy(szBuffer, szCopy, sizeof(szCopy));
}
}
- printf(szBuffer);
+ printf("%s", szBuffer);
}
-
+
list_free(&substPtr);
}
fclose(fp);
return 0;
}
+BOOL FileExists(LPCTSTR szPath)
+{
+#ifndef INVALID_FILE_ATTRIBUTES
+ #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+ DWORD pathAttr = GetFileAttributes(szPath);
+ return (pathAttr != INVALID_FILE_ATTRIBUTES &&
+ !(pathAttr & FILE_ATTRIBUTE_DIRECTORY));
+}
+
+
/*
* QualifyPath --
*
@@ -672,17 +705,105 @@ QualifyPath(
const char *szPath)
{
char szCwd[MAX_PATH + 1];
- char szTmp[MAX_PATH + 1];
- char *p;
- GetCurrentDirectory(MAX_PATH, szCwd);
- while ((p = strchr(szPath, '/')) && *p)
- *p = '\\';
- PathCombine(szTmp, szCwd, szPath);
- PathCanonicalize(szCwd, szTmp);
+
+ GetFullPathName(szPath, sizeof(szCwd)-1, szCwd, NULL);
printf("%s\n", szCwd);
return 0;
}
+/*
+ * Implements LocateDependency for a single directory. See that command
+ * for an explanation.
+ * Returns 0 if found after printing the directory.
+ * Returns 1 if not found but no errors.
+ * Returns 2 on any kind of error
+ * Basically, these are used as exit codes for the process.
+ */
+static int LocateDependencyHelper(const char *dir, const char *keypath)
+{
+ HANDLE hSearch;
+ char path[MAX_PATH+1];
+ size_t dirlen;
+ int keylen, ret;
+ WIN32_FIND_DATA finfo;
+
+ if (dir == NULL || keypath == NULL)
+ return 2; /* Have no real error reporting mechanism into nmake */
+ dirlen = strlen(dir);
+ if ((dirlen + 3) > sizeof(path))
+ return 2;
+ strncpy(path, dir, dirlen);
+ strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */
+ keylen = strlen(keypath);
+
+#if 0 /* This function is not available in Visual C++ 6 */
+ /*
+ * Use numerics 0 -> FindExInfoStandard,
+ * 1 -> FindExSearchLimitToDirectories,
+ * as these are not defined in Visual C++ 6
+ */
+ hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0);
+#else
+ hSearch = FindFirstFile(path, &finfo);
+#endif
+ if (hSearch == INVALID_HANDLE_VALUE)
+ return 1; /* Not found */
+
+ /* Loop through all subdirs checking if the keypath is under there */
+ ret = 1; /* Assume not found */
+ do {
+ int sublen;
+ /*
+ * We need to check it is a directory despite the
+ * FindExSearchLimitToDirectories in the above call. See SDK docs
+ */
+ if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ continue;
+ sublen = strlen(finfo.cFileName);
+ if ((dirlen+1+sublen+1+keylen+1) > sizeof(path))
+ continue; /* Path does not fit, assume not matched */
+ strncpy(path+dirlen+1, finfo.cFileName, sublen);
+ path[dirlen+1+sublen] = '\\';
+ strncpy(path+dirlen+1+sublen+1, keypath, keylen+1);
+ if (FileExists(path)) {
+ /* Found a match, print to stdout */
+ path[dirlen+1+sublen] = '\0';
+ QualifyPath(path);
+ ret = 0;
+ break;
+ }
+ } while (FindNextFile(hSearch, &finfo));
+ FindClose(hSearch);
+ return ret;
+}
+
+/*
+ * LocateDependency --
+ *
+ * Locates a dependency for a package.
+ * keypath - a relative path within the package directory
+ * that is used to confirm it is the correct directory.
+ * The search path for the package directory is currently only
+ * the parent and grandparent of the current working directory.
+ * If found, the command prints
+ * name_DIRPATH=
+ * and returns 0. If not found, does not print anything and returns 1.
+ */
+static int LocateDependency(const char *keypath)
+{
+ size_t i;
+ int ret;
+ static const char *paths[] = {"..", "..\\..", "..\\..\\.."};
+
+ for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) {
+ ret = LocateDependencyHelper(paths[i], keypath);
+ if (ret == 0)
+ return ret;
+ }
+ return ret;
+}
+
+
/*
* Local variables:
* mode: c
diff --git a/sqlite/autoconf/tea/win/rules-ext.vc b/sqlite/autoconf/tea/win/rules-ext.vc
new file mode 100644
index 00000000..479720a4
--- /dev/null
+++ b/sqlite/autoconf/tea/win/rules-ext.vc
@@ -0,0 +1,123 @@
+# This file should only be included in makefiles for Tcl extensions,
+# NOT in the makefile for Tcl itself.
+
+!ifndef _RULES_EXT_VC
+
+# We need to run from the directory the parent makefile is located in.
+# nmake does not tell us what makefile was used to invoke it so parent
+# makefile has to set the MAKEFILEVC macro or we just make a guess and
+# warn if we think that is not the case.
+!if "$(MAKEFILEVC)" == ""
+
+!if exist("$(PROJECT).vc")
+MAKEFILEVC = $(PROJECT).vc
+!elseif exist("makefile.vc")
+MAKEFILEVC = makefile.vc
+!endif
+!endif # "$(MAKEFILEVC)" == ""
+
+!if !exist("$(MAKEFILEVC)")
+MSG = ^
+You must run nmake from the directory containing the project makefile.^
+If you are doing that and getting this message, set the MAKEFILEVC^
+macro to the name of the project makefile.
+!message WARNING: $(MSG)
+!endif
+
+!if "$(PROJECT)" == "tcl"
+!error The rules-ext.vc file is not intended for Tcl itself.
+!endif
+
+# We extract version numbers using the nmakehlp program. For now use
+# the local copy of nmakehlp. Once we locate Tcl, we will use that
+# one if it is newer.
+!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
+!if [$(CC) -nologo -DNDEBUG "nmakehlp.c" -link -subsystem:console > nul]
+!endif
+!else
+!if [copy x86_64-w64-mingw32-nmakehlp.exe nmakehlp.exe >NUL]
+!endif
+!endif
+
+# First locate the Tcl directory that we are working with.
+!if "$(TCLDIR)" != ""
+
+_RULESDIR = $(TCLDIR:/=\)
+
+!else
+
+# If an installation path is specified, that is also the Tcl directory.
+# Also Tk never builds against an installed Tcl, it needs Tcl sources
+!if defined(INSTALLDIR) && "$(PROJECT)" != "tk"
+_RULESDIR=$(INSTALLDIR:/=\)
+!else
+# Locate Tcl sources
+!if [echo _RULESDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+_RULESDIR = ..\..\tcl
+!else
+!include nmakehlp.out
+!endif
+
+!endif # defined(INSTALLDIR)....
+
+!endif # ifndef TCLDIR
+
+# Now look for the targets.vc file under the Tcl root. Note we check this
+# file and not rules.vc because the latter also exists on older systems.
+!if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl
+_RULESDIR = $(_RULESDIR)\lib\nmake
+!elseif exist("$(_RULESDIR)\win\targets.vc") # Building against Tcl sources
+_RULESDIR = $(_RULESDIR)\win
+!else
+# If we have not located Tcl's targets file, most likely we are compiling
+# against an older version of Tcl and so must use our own support files.
+_RULESDIR = .
+!endif
+
+!if "$(_RULESDIR)" != "."
+# Potentially using Tcl's support files. If this extension has its own
+# nmake support files, need to compare the versions and pick newer.
+
+!if exist("rules.vc") # The extension has its own copy
+
+!if [echo TCL_RULES_MAJOR = \> versions.vc] \
+ && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc]
+!endif
+!if [echo TCL_RULES_MINOR = \>> versions.vc] \
+ && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc]
+!endif
+
+!if [echo OUR_RULES_MAJOR = \>> versions.vc] \
+ && [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc]
+!endif
+!if [echo OUR_RULES_MINOR = \>> versions.vc] \
+ && [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc]
+!endif
+!include versions.vc
+# We have a newer version of the support files, use them
+!if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR))
+_RULESDIR = .
+!endif
+
+!endif # if exist("rules.vc")
+
+!endif # if $(_RULESDIR) != "."
+
+# Let rules.vc know what copy of nmakehlp.c to use.
+NMAKEHLPC = $(_RULESDIR)\nmakehlp.c
+
+# Get rid of our internal defines before calling rules.vc
+!undef TCL_RULES_MAJOR
+!undef TCL_RULES_MINOR
+!undef OUR_RULES_MAJOR
+!undef OUR_RULES_MINOR
+
+!if exist("$(_RULESDIR)\rules.vc")
+!message *** Using $(_RULESDIR)\rules.vc
+!include "$(_RULESDIR)\rules.vc"
+!else
+!error *** Could not locate rules.vc in $(_RULESDIR)
+!endif
+
+!endif # _RULES_EXT_VC
\ No newline at end of file
diff --git a/sqlite/autoconf/tea/win/rules.vc b/sqlite/autoconf/tea/win/rules.vc
index 99471053..8ecce0e1 100644
--- a/sqlite/autoconf/tea/win/rules.vc
+++ b/sqlite/autoconf/tea/win/rules.vc
@@ -1,31 +1,139 @@
-#------------------------------------------------------------------------------
+#------------------------------------------------------------- -*- makefile -*-
# rules.vc --
#
-# Microsoft Visual C++ makefile include for decoding the commandline
-# macros. This file does not need editing to build Tcl.
+# Part of the nmake based build system for Tcl and its extensions.
+# This file does all the hard work in terms of parsing build options,
+# compiler switches, defining common targets and macros. The Tcl makefile
+# directly includes this. Extensions include it via "rules-ext.vc".
+#
+# See TIP 477 (https://core.tcl-lang.org/tips/doc/main/tip/477.md) for
+# detailed documentation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# Copyright (c) 2001-2003 David Gravereaux.
# Copyright (c) 2003-2008 Patrick Thoyts
+# Copyright (c) 2017 Ashok P. Nadkarni
#------------------------------------------------------------------------------
!ifndef _RULES_VC
_RULES_VC = 1
-cc32 = $(CC) # built-in default.
-link32 = link
-lib32 = lib
-rc32 = $(RC) # built-in default.
+# The following macros define the version of the rules.vc nmake build system
+# For modifications that are not backward-compatible, you *must* change
+# the major version.
+RULES_VERSION_MAJOR = 1
+RULES_VERSION_MINOR = 12
-!ifndef INSTALLDIR
-### Assume the normal default.
-_INSTALLDIR = C:\Program Files\Tcl
+# The PROJECT macro must be defined by parent makefile.
+!if "$(PROJECT)" == ""
+!error *** Error: Macro PROJECT not defined! Please define it before including rules.vc
+!endif
+
+!if "$(PRJ_PACKAGE_TCLNAME)" == ""
+PRJ_PACKAGE_TCLNAME = $(PROJECT)
+!endif
+
+# Also special case Tcl and Tk to save some typing later
+DOING_TCL = 0
+DOING_TK = 0
+!if "$(PROJECT)" == "tcl"
+DOING_TCL = 1
+!elseif "$(PROJECT)" == "tk"
+DOING_TK = 1
+!endif
+
+!ifndef NEED_TK
+# Backwards compatibility
+!ifdef PROJECT_REQUIRES_TK
+NEED_TK = $(PROJECT_REQUIRES_TK)
!else
-### Fix the path separators.
-_INSTALLDIR = $(INSTALLDIR:/=\)
+NEED_TK = 0
+!endif
+!endif
+
+!ifndef NEED_TCL_SOURCE
+NEED_TCL_SOURCE = 0
+!endif
+
+!ifdef NEED_TK_SOURCE
+!if $(NEED_TK_SOURCE)
+NEED_TK = 1
!endif
+!else
+NEED_TK_SOURCE = 0
+!endif
+
+################################################################
+# Nmake is a pretty weak environment in syntax and capabilities
+# so this file is necessarily verbose. It's broken down into
+# the following parts.
+#
+# 0. Sanity check that compiler environment is set up and initialize
+# any built-in settings from the parent makefile
+# 1. First define the external tools used for compiling, copying etc.
+# as this is independent of everything else.
+# 2. Figure out our build structure in terms of the directory, whether
+# we are building Tcl or an extension, etc.
+# 3. Determine the compiler and linker versions
+# 4. Build the nmakehlp helper application
+# 5. Determine the supported compiler options and features
+# 6. Extract Tcl, Tk, and possibly extensions, version numbers from the
+# headers
+# 7. Parse the OPTS macro value for user-specified build configuration
+# 8. Parse the STATS macro value for statistics instrumentation
+# 9. Parse the CHECKS macro for additional compilation checks
+# 10. Based on this selected configuration, construct the output
+# directory and file paths
+# 11. Construct the paths where the package is to be installed
+# 12. Set up the actual options passed to compiler and linker based
+# on the information gathered above.
+# 13. Define some standard build targets and implicit rules. These may
+# be optionally disabled by the parent makefile.
+# 14. (For extensions only.) Compare the configuration of the target
+# Tcl and the extensions and warn against discrepancies.
+#
+# One final note about the macro names used. They are as they are
+# for historical reasons. We would like legacy extensions to
+# continue to work with this make include file so be wary of
+# changing them for consistency or clarity.
+
+# 0. Sanity check compiler environment
+
+# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
+# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)
+
+!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
+MSG = ^
+Visual C++ compiler environment not initialized.
+!error $(MSG)
+!endif
+
+# We need to run from the directory the parent makefile is located in.
+# nmake does not tell us what makefile was used to invoke it so parent
+# makefile has to set the MAKEFILEVC macro or we just make a guess and
+# warn if we think that is not the case.
+!if "$(MAKEFILEVC)" == ""
+
+!if exist("$(PROJECT).vc")
+MAKEFILEVC = $(PROJECT).vc
+!elseif exist("makefile.vc")
+MAKEFILEVC = makefile.vc
+!endif
+!endif # "$(MAKEFILEVC)" == ""
+
+!if !exist("$(MAKEFILEVC)")
+MSG = ^
+You must run nmake from the directory containing the project makefile.^
+If you are doing that and getting this message, set the MAKEFILEVC^
+macro to the name of the project makefile.
+!message WARNING: $(MSG)
+!endif
+
+
+################################################################
+# 1. Define external programs being used
#----------------------------------------------------------
# Set the proper copy method to avoid overwrite questions
@@ -33,28 +141,291 @@ _INSTALLDIR = $(INSTALLDIR:/=\)
# "delete all" method.
#----------------------------------------------------------
-!if "$(OS)" == "Windows_NT"
RMDIR = rmdir /S /Q
-ERRNULL = 2>NUL
-!if ![ver | find "4.0" > nul]
-CPY = echo y | xcopy /i >NUL
-COPY = copy >NUL
-!else
CPY = xcopy /i /y >NUL
+CPYDIR = xcopy /e /i /y >NUL
COPY = copy /y >NUL
+MKDIR = mkdir
+
+######################################################################
+# 2. Figure out our build environment in terms of what we're building.
+#
+# (a) Tcl itself
+# (b) Tk
+# (c) a Tcl extension using libraries/includes from an *installed* Tcl
+# (d) a Tcl extension using libraries/includes from Tcl source directory
+#
+# This last is needed because some extensions still need
+# some Tcl interfaces that are not publicly exposed.
+#
+# The fragment will set the following macros:
+# ROOT - root of this module sources
+# COMPATDIR - source directory that holds compatibility sources
+# DOCDIR - source directory containing documentation files
+# GENERICDIR - platform-independent source directory
+# WIN_DIR - Windows-specific source directory
+# TESTDIR - directory containing test files
+# TOOLSDIR - directory containing build tools
+# _TCLDIR - root of the Tcl installation OR the Tcl sources. Not set
+# when building Tcl itself.
+# _INSTALLDIR - native form of the installation path. For Tcl
+# this will be the root of the Tcl installation. For extensions
+# this will be the lib directory under the root.
+# TCLINSTALL - set to 1 if _TCLDIR refers to
+# headers and libraries from an installed Tcl, and 0 if built against
+# Tcl sources. Not set when building Tcl itself. Yes, not very well
+# named.
+# _TCL_H - native path to the tcl.h file
+#
+# If Tk is involved, also sets the following
+# _TKDIR - native form Tk installation OR Tk source. Not set if building
+# Tk itself.
+# TKINSTALL - set 1 if _TKDIR refers to installed Tk and 0 if Tk sources
+# _TK_H - native path to the tk.h file
+
+# Root directory for sources and assumed subdirectories
+ROOT = $(MAKEDIR)\..
+# The following paths CANNOT have spaces in them as they appear on the
+# left side of implicit rules.
+!ifndef COMPATDIR
+COMPATDIR = $(ROOT)\compat
+!endif
+!ifndef DOCDIR
+DOCDIR = $(ROOT)\doc
+!endif
+!ifndef GENERICDIR
+GENERICDIR = $(ROOT)\generic
+!endif
+!ifndef TOOLSDIR
+TOOLSDIR = $(ROOT)\tools
+!endif
+!ifndef TESTDIR
+TESTDIR = $(ROOT)\tests
+!endif
+!ifndef LIBDIR
+!if exist("$(ROOT)\library")
+LIBDIR = $(ROOT)\library
+!else
+LIBDIR = $(ROOT)\lib
!endif
-!else # "$(OS)" != "Windows_NT"
-CPY = xcopy /i >_JUNK.OUT # On Win98 NUL does not work here.
-COPY = copy >_JUNK.OUT # On Win98 NUL does not work here.
-RMDIR = deltree /Y
-NULL = \NUL # Used in testing directory existence
-ERRNULL = >NUL # Win9x shell cannot redirect stderr
!endif
-MKDIR = mkdir
+!ifndef DEMODIR
+!if exist("$(LIBDIR)\demos")
+DEMODIR = $(LIBDIR)\demos
+!else
+DEMODIR = $(ROOT)\demos
+!endif
+!endif # ifndef DEMODIR
+# Do NOT use WINDIR because it is Windows internal environment
+# variable to point to c:\windows!
+WIN_DIR = $(ROOT)\win
-#------------------------------------------------------------------------------
-# Determine the host and target architectures and compiler version.
-#------------------------------------------------------------------------------
+!ifndef RCDIR
+!if exist("$(WIN_DIR)\rc")
+RCDIR = $(WIN_DIR)\rc
+!else
+RCDIR = $(WIN_DIR)
+!endif
+!endif
+RCDIR = $(RCDIR:/=\)
+
+# The target directory where the built packages and binaries will be installed.
+# INSTALLDIR is the (optional) path specified by the user.
+# _INSTALLDIR is INSTALLDIR using the backslash separator syntax
+!ifdef INSTALLDIR
+### Fix the path separators.
+_INSTALLDIR = $(INSTALLDIR:/=\)
+!else
+### Assume the normal default.
+_INSTALLDIR = $(HOMEDRIVE)\Tcl
+!endif
+
+!if $(DOING_TCL)
+
+# BEGIN Case 2(a) - Building Tcl itself
+
+# Only need to define _TCL_H
+_TCL_H = ..\generic\tcl.h
+
+# END Case 2(a) - Building Tcl itself
+
+!elseif $(DOING_TK)
+
+# BEGIN Case 2(b) - Building Tk
+
+TCLINSTALL = 0 # Tk always builds against Tcl source, not an installed Tcl
+!if "$(TCLDIR)" == ""
+!if [echo TCLDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+!error *** Could not locate Tcl source directory.
+!endif
+!include nmakehlp.out
+!endif # TCLDIR == ""
+
+_TCLDIR = $(TCLDIR:/=\)
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!if !exist("$(_TCL_H)")
+!error Could not locate tcl.h. Please set the TCLDIR macro to point to the Tcl *source* directory.
+!endif
+
+_TK_H = ..\generic\tk.h
+
+# END Case 2(b) - Building Tk
+
+!else
+
+# BEGIN Case 2(c) or (d) - Building an extension other than Tk
+
+# If command line has specified Tcl location through TCLDIR, use it
+# else default to the INSTALLDIR setting
+!if "$(TCLDIR)" != ""
+
+_TCLDIR = $(TCLDIR:/=\)
+!if exist("$(_TCLDIR)\include\tcl.h") # Case 2(c) with TCLDIR defined
+TCLINSTALL = 1
+_TCL_H = $(_TCLDIR)\include\tcl.h
+!elseif exist("$(_TCLDIR)\generic\tcl.h") # Case 2(d) with TCLDIR defined
+TCLINSTALL = 0
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!endif
+
+!else # # Case 2(c) for extensions with TCLDIR undefined
+
+# Need to locate Tcl depending on whether it needs Tcl source or not.
+# If we don't, check the INSTALLDIR for an installed Tcl first
+
+!if exist("$(_INSTALLDIR)\include\tcl.h") && !$(NEED_TCL_SOURCE)
+
+TCLINSTALL = 1
+TCLDIR = $(_INSTALLDIR)\..
+# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
+# later so the \.. accounts for the /lib
+_TCLDIR = $(_INSTALLDIR)\..
+_TCL_H = $(_TCLDIR)\include\tcl.h
+
+!else # exist(...) && !$(NEED_TCL_SOURCE)
+
+!if [echo _TCLDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+!error *** Could not locate Tcl source directory.
+!endif
+!include nmakehlp.out
+TCLINSTALL = 0
+TCLDIR = $(_TCLDIR)
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+
+!endif # exist(...) && !$(NEED_TCL_SOURCE)
+
+!endif # TCLDIR
+
+!ifndef _TCL_H
+MSG =^
+Failed to find tcl.h. The TCLDIR macro is set incorrectly or is not set and default path does not contain tcl.h.
+!error $(MSG)
+!endif
+
+# Now do the same to locate Tk headers and libs if project requires Tk
+!if $(NEED_TK)
+
+!if "$(TKDIR)" != ""
+
+_TKDIR = $(TKDIR:/=\)
+!if exist("$(_TKDIR)\include\tk.h")
+TKINSTALL = 1
+_TK_H = $(_TKDIR)\include\tk.h
+!elseif exist("$(_TKDIR)\generic\tk.h")
+TKINSTALL = 0
+_TK_H = $(_TKDIR)\generic\tk.h
+!endif
+
+!else # TKDIR not defined
+
+# Need to locate Tcl depending on whether it needs Tcl source or not.
+# If we don't, check the INSTALLDIR for an installed Tcl first
+
+!if exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+TKINSTALL = 1
+# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
+# later so the \.. accounts for the /lib
+_TKDIR = $(_INSTALLDIR)\..
+_TK_H = $(_TKDIR)\include\tk.h
+TKDIR = $(_TKDIR)
+
+!else # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+!if [echo _TKDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tk.h >> nmakehlp.out]
+!error *** Could not locate Tk source directory.
+!endif
+!include nmakehlp.out
+TKINSTALL = 0
+TKDIR = $(_TKDIR)
+_TK_H = $(_TKDIR)\generic\tk.h
+
+!endif # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+!endif # TKDIR
+
+!ifndef _TK_H
+MSG =^
+Failed to find tk.h. The TKDIR macro is set incorrectly or is not set and default path does not contain tk.h.
+!error $(MSG)
+!endif
+
+!endif # NEED_TK
+
+!if $(NEED_TCL_SOURCE) && $(TCLINSTALL)
+MSG = ^
+*** Warning: This extension requires the source distribution of Tcl.^
+*** Please set the TCLDIR macro to point to the Tcl sources.
+!error $(MSG)
+!endif
+
+!if $(NEED_TK_SOURCE)
+!if $(TKINSTALL)
+MSG = ^
+*** Warning: This extension requires the source distribution of Tk.^
+*** Please set the TKDIR macro to point to the Tk sources.
+!error $(MSG)
+!endif
+!endif
+
+
+# If INSTALLDIR set to Tcl installation root dir then reset to the
+# lib dir for installing extensions
+!if exist("$(_INSTALLDIR)\include\tcl.h")
+_INSTALLDIR=$(_INSTALLDIR)\lib
+!endif
+
+# END Case 2(c) or (d) - Building an extension
+!endif # if $(DOING_TCL)
+
+################################################################
+# 3. Determine compiler version and architecture
+# In this section, we figure out the compiler version and the
+# architecture for which we are building. This sets the
+# following macros:
+# VCVERSION - the internal compiler version as 1200, 1400, 1910 etc.
+# This is also printed by the compiler in dotted form 19.10 etc.
+# VCVER - the "marketing version", for example Visual C++ 6 for internal
+# compiler version 1200. This is kept only for legacy reasons as it
+# does not make sense for recent Microsoft compilers. Only used for
+# output directory names.
+# ARCH - set to IX86, ARM64 or AMD64 depending on 32- or 64-bit target
+# NATIVE_ARCH - set to IX86, ARM64 or AMD64 for the host machine
+# MACHINE - same as $(ARCH) - legacy
+# _VC_MANIFEST_EMBED_{DLL,EXE} - commands for embedding a manifest if needed
+
+cc32 = $(CC) # built-in default.
+link32 = link
+lib32 = lib
+rc32 = $(RC) # built-in default.
+
+#----------------------------------------------------------------
+# Figure out the compiler architecture and version by writing
+# the C macros to a file, preprocessing them with the C
+# preprocessor and reading back the created file
_HASH=^#
_VC_MANIFEST_EMBED_EXE=
@@ -65,19 +436,70 @@ VCVER=0
&& ![echo ARCH=IX86 >> vercl.x] \
&& ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
&& ![echo ARCH=AMD64 >> vercl.x] \
+ && ![echo $(_HASH)elif defined(_M_ARM64) >> vercl.x] \
+ && ![echo ARCH=ARM64 >> vercl.x] \
&& ![echo $(_HASH)endif >> vercl.x] \
- && ![cl -nologo -TC -P vercl.x $(ERRNULL)]
+ && ![$(cc32) -nologo -TC -P vercl.x 2>NUL]
!include vercl.i
+!if $(VCVERSION) < 1900
!if ![echo VCVER= ^\> vercl.vc] \
&& ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
!include vercl.vc
!endif
+!else
+# The simple calculation above does not apply to new Visual Studio releases
+# Keep the compiler version in its native form.
+VCVER = $(VCVERSION)
+!endif
!endif
-!if ![del $(ERRNUL) /q/f vercl.x vercl.i vercl.vc]
+
+!if ![del 2>NUL /q/f vercl.x vercl.i vercl.vc]
!endif
+#----------------------------------------------------------------
+# The MACHINE macro is used by legacy makefiles so set it as well
+!ifdef MACHINE
+!if "$(MACHINE)" == "x86"
+!undef MACHINE
+MACHINE = IX86
+!elseif "$(MACHINE)" == "arm64"
+!undef MACHINE
+MACHINE = ARM64
+!elseif "$(MACHINE)" == "x64"
+!undef MACHINE
+MACHINE = AMD64
+!endif
+!if "$(MACHINE)" != "$(ARCH)"
+!error Specified MACHINE macro $(MACHINE) does not match detected target architecture $(ARCH).
+!endif
+!else
+MACHINE=$(ARCH)
+!endif
+
+#---------------------------------------------------------------
+# The PLATFORM_IDENTIFY macro matches the values returned by
+# the Tcl platform::identify command
+!if "$(MACHINE)" == "AMD64"
+PLATFORM_IDENTIFY = win32-x86_64
+!elseif "$(MACHINE)" == "ARM64"
+PLATFORM_IDENTIFY = win32-arm
+!else
+PLATFORM_IDENTIFY = win32-ix86
+!endif
+
+# The MULTIPLATFORM macro controls whether binary extensions are installed
+# in platform-specific directories. Intended to be set/used by extensions.
+!ifndef MULTIPLATFORM_INSTALL
+MULTIPLATFORM_INSTALL = 0
+!endif
+
+#------------------------------------------------------------
+# Figure out the *host* architecture by reading the registry
+
!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86]
NATIVE_ARCH=IX86
+!elseif ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i ARM | findstr /i 64-bit]
+NATIVE_ARCH=ARM64
!else
NATIVE_ARCH=AMD64
!endif
@@ -88,185 +510,408 @@ _VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -ou
_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
!endif
-!ifndef MACHINE
-MACHINE=$(ARCH)
-!endif
-
-!ifndef CFG_ENCODING
-CFG_ENCODING = \"cp1252\"
-!endif
-
-!message ===============================================================================
+################################################################
+# 4. Build the nmakehlp program
+# This is a helper app we need to overcome nmake's limiting
+# environment. We will call out to it to get various bits of
+# information about supported compiler options etc.
+#
+# Tcl itself will always use the nmakehlp.c program which is
+# in its own source. It will be kept updated there.
+#
+# Extensions built against an installed Tcl will use the installed
+# copy of Tcl's nmakehlp.c if there is one and their own version
+# otherwise. In the latter case, they would also be using their own
+# rules.vc. Note that older versions of Tcl do not install nmakehlp.c
+# or rules.vc.
+#
+# Extensions built against Tcl sources will use the one from the Tcl source.
+#
+# When building an extension using a sufficiently new version of Tcl,
+# rules-ext.vc will define NMAKEHLPC appropriately to point to the
+# copy of nmakehlp.c to be used.
-#----------------------------------------------------------
-# build the helper app we need to overcome nmake's limiting
-# environment.
-#----------------------------------------------------------
+!ifndef NMAKEHLPC
+# Default to the one in the current directory (the extension's own nmakehlp.c)
+NMAKEHLPC = nmakehlp.c
-!if !exist(nmakehlp.exe)
-!if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul]
+!if !$(DOING_TCL)
+!if $(TCLINSTALL)
+!if exist("$(_TCLDIR)\lib\nmake\nmakehlp.c")
+NMAKEHLPC = $(_TCLDIR)\lib\nmake\nmakehlp.c
!endif
+!else # !$(TCLINSTALL)
+!if exist("$(_TCLDIR)\win\nmakehlp.c")
+NMAKEHLPC = $(_TCLDIR)\win\nmakehlp.c
!endif
+!endif # $(TCLINSTALL)
+!endif # !$(DOING_TCL)
-#----------------------------------------------------------
-# Test for compiler features
-#----------------------------------------------------------
+!endif # NMAKEHLPC
-### test for optimizations
-!if [nmakehlp -c -Ot]
-!message *** Compiler has 'Optimizations'
-OPTIMIZING = 1
+# We always build nmakehlp even if it exists since we do not know
+# what source it was built from.
+!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
+!if [$(cc32) -nologo "$(NMAKEHLPC)" -link -subsystem:console > nul]
+!endif
!else
-!message *** Compiler does not have 'Optimizations'
-OPTIMIZING = 0
+!if [copy $(NMAKEHLPC:nmakehlp.c=x86_64-w64-mingw32-nmakehlp.exe) nmakehlp.exe >NUL]
!endif
-
-OPTIMIZATIONS =
-
-!if [nmakehlp -c -Ot]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Ot
!endif
-!if [nmakehlp -c -Oi]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Oi
-!endif
+################################################################
+# 5. Test for compiler features
+# Visual C++ compiler options have changed over the years. Check
+# which options are supported by the compiler in use.
+#
+# The following macros are set:
+# OPTIMIZATIONS - the compiler flags to be used for optimized builds
+# DEBUGFLAGS - the compiler flags to be used for debug builds
+# LINKERFLAGS - Flags passed to the linker
+#
+# Note that these are the compiler settings *available*, not those
+# that will be *used*. The latter depends on the OPTS macro settings
+# which we have not yet parsed.
+#
+# Also note that some of the flags in OPTIMIZATIONS are not really
+# related to optimization. They are placed there only for legacy reasons
+# as some extensions expect them to be included in that macro.
+# -Op improves float consistency. Note only needed for older compilers
+# Newer compilers do not need or support this option.
!if [nmakehlp -c -Op]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Op
+FPOPTS = -Op
!endif
+# Strict floating point semantics - present in newer compilers in lieu of -Op
!if [nmakehlp -c -fp:strict]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -fp:strict
+FPOPTS = $(FPOPTS) -fp:strict
+!endif
+
+!if "$(MACHINE)" == "IX86"
+### test for pentium errata
+!if [nmakehlp -c -QI0f]
+!message *** Compiler has 'Pentium 0x0f fix'
+FPOPTS = $(FPOPTS) -QI0f
+!else
+!message *** Compiler does not have 'Pentium 0x0f fix'
+!endif
!endif
-!if [nmakehlp -c -Gs]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -Gs
+### test for optimizations
+# /O2 optimization includes /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy as per
+# documentation. Note we do NOT want /Gs as that inserts a _chkstk
+# stack probe at *every* function entry, not just those with more than
+# a page of stack allocation resulting in a performance hit. However,
+# /O2 documentation is misleading as its stack probes are simply the
+# default page size locals allocation probes and not what is implied
+# by an explicit /Gs option.
+
+OPTIMIZATIONS = $(FPOPTS)
+
+!if [nmakehlp -c -O2]
+OPTIMIZING = 1
+OPTIMIZATIONS = $(OPTIMIZATIONS) -O2
+!else
+# Legacy, really. All modern compilers support this
+!message *** Compiler does not have 'Optimizations'
+OPTIMIZING = 0
!endif
+# Checks for buffer overflows in local arrays
!if [nmakehlp -c -GS]
OPTIMIZATIONS = $(OPTIMIZATIONS) -GS
!endif
+# Link time optimization. Note that this option (potentially) makes
+# generated libraries only usable by the specific VC++ version that
+# created it. Requires /LTCG linker option
!if [nmakehlp -c -GL]
OPTIMIZATIONS = $(OPTIMIZATIONS) -GL
+CC_GL_OPT_ENABLED = 1
+!else
+# In newer compilers -GL and -YX are incompatible.
+!if [nmakehlp -c -YX]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -YX
!endif
+!endif # [nmakehlp -c -GL]
-DEBUGFLAGS =
+DEBUGFLAGS = $(FPOPTS)
+# Run time error checks. Not available or valid in a release, non-debug build
+# RTC is for modern compilers, -GZ is legacy
!if [nmakehlp -c -RTC1]
DEBUGFLAGS = $(DEBUGFLAGS) -RTC1
!elseif [nmakehlp -c -GZ]
DEBUGFLAGS = $(DEBUGFLAGS) -GZ
!endif
-COMPILERFLAGS =-W3 -DUNICODE -D_UNICODE
+#----------------------------------------------------------------
+# Linker flags
-# In v13 -GL and -YX are incompatible.
-!if [nmakehlp -c -YX]
-!if ![nmakehlp -c -GL]
-OPTIMIZATIONS = $(OPTIMIZATIONS) -YX
-!endif
+# LINKER_TESTFLAGS are for internal use when we call nmakehlp to test
+# if the linker supports a specific option. Without these flags link will
+# return "LNK1561: entry point must be defined" error compiling from VS-IDE:
+# They are not passed through to the actual application / extension
+# link rules.
+!ifndef LINKER_TESTFLAGS
+LINKER_TESTFLAGS = /DLL /NOENTRY /OUT:nmakehlp.out
!endif
-!if "$(MACHINE)" == "IX86"
-### test for pentium errata
-!if [nmakehlp -c -QI0f]
-!message *** Compiler has 'Pentium 0x0f fix'
-COMPILERFLAGS = $(COMPILERFLAGS) -QI0f
-!else
-!message *** Compiler does not have 'Pentium 0x0f fix'
+LINKERFLAGS =
+
+# If compiler has enabled link time optimization, linker must too with -ltcg
+!ifdef CC_GL_OPT_ENABLED
+!if [nmakehlp -l -ltcg $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS) -ltcg
+!endif
+!endif
+
+
+################################################################
+# 6. Extract various version numbers from headers
+# For Tcl and Tk, version numbers are extracted from tcl.h and tk.h
+# respectively. For extensions, versions are extracted from the
+# configure.in or configure.ac from the TEA configuration if it
+# exists, and unset otherwise.
+# Sets the following macros:
+# TCL_MAJOR_VERSION
+# TCL_MINOR_VERSION
+# TCL_RELEASE_SERIAL
+# TCL_PATCH_LEVEL
+# TCL_PATCH_LETTER
+# TCL_VERSION
+# TK_MAJOR_VERSION
+# TK_MINOR_VERSION
+# TK_RELEASE_SERIAL
+# TK_PATCH_LEVEL
+# TK_PATCH_LETTER
+# TK_VERSION
+# DOTVERSION - set as (for example) 2.5
+# VERSION - set as (for example 25)
+#--------------------------------------------------------------
+
+!if [echo REM = This file is generated from rules.vc > versions.vc]
!endif
+!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" "define TCL_MAJOR_VERSION" >> versions.vc]
+!endif
+!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_RELEASE_SERIAL = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_RELEASE_SERIAL >> versions.vc]
+!endif
+!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
!endif
-!if "$(MACHINE)" == "IA64"
-### test for Itanium errata
-!if [nmakehlp -c -QIA64_Bx]
-!message *** Compiler has 'B-stepping errata workarounds'
-COMPILERFLAGS = $(COMPILERFLAGS) -QIA64_Bx
-!else
-!message *** Compiler does not have 'B-stepping errata workarounds'
+!if defined(_TK_H)
+!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) "define TK_MAJOR_VERSION" >> versions.vc]
+!endif
+!if [echo TK_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
!endif
+!if [echo TK_RELEASE_SERIAL = \>> versions.vc] \
+ && [nmakehlp -V "$(_TK_H)" TK_RELEASE_SERIAL >> versions.vc]
!endif
+!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
+!endif
+!endif # _TK_H
-!if "$(MACHINE)" == "IX86"
-### test for -align:4096, when align:512 will do.
-!if [nmakehlp -l -opt:nowin98]
-!message *** Linker has 'Win98 alignment problem'
-ALIGN98_HACK = 1
+!include versions.vc
+
+TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+TCL_DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+!if [nmakehlp -f $(TCL_PATCH_LEVEL) "a"]
+TCL_PATCH_LETTER = a
+!elseif [nmakehlp -f $(TCL_PATCH_LEVEL) "b"]
+TCL_PATCH_LETTER = b
!else
-!message *** Linker does not have 'Win98 alignment problem'
-ALIGN98_HACK = 0
+TCL_PATCH_LETTER = .
!endif
+
+!if defined(_TK_H)
+
+TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
+TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+!if [nmakehlp -f $(TK_PATCH_LEVEL) "a"]
+TK_PATCH_LETTER = a
+!elseif [nmakehlp -f $(TK_PATCH_LEVEL) "b"]
+TK_PATCH_LETTER = b
!else
-ALIGN98_HACK = 0
+TK_PATCH_LETTER = .
!endif
-LINKERFLAGS =
-
-!if [nmakehlp -l -ltcg]
-LINKERFLAGS =-ltcg
!endif
-#----------------------------------------------------------
-# Decode the options requested.
-#----------------------------------------------------------
+# Set DOTVERSION and VERSION
+!if $(DOING_TCL)
+
+DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+VERSION = $(TCL_VERSION)
+
+!elseif $(DOING_TK)
-!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"]
+DOTVERSION = $(TK_DOTVERSION)
+VERSION = $(TK_VERSION)
+
+!else # Doing a non-Tk extension
+
+# If parent makefile has not defined DOTVERSION, try to get it from TEA
+# first from a configure.in file, and then from configure.ac
+!ifndef DOTVERSION
+!if [echo DOTVERSION = \> versions.vc] \
+ || [nmakehlp -V $(ROOT)\configure.in ^[$(PROJECT)^] >> versions.vc]
+!if [echo DOTVERSION = \> versions.vc] \
+ || [nmakehlp -V $(ROOT)\configure.ac ^[$(PROJECT)^] >> versions.vc]
+!error *** Could not figure out extension version. Please define DOTVERSION in parent makefile before including rules.vc.
+!endif
+!endif
+!include versions.vc
+!endif # DOTVERSION
+VERSION = $(DOTVERSION:.=)
+
+!endif # $(DOING_TCL) ... etc.
+
+# Windows RC files have 3 version components. Ensure this irrespective
+# of how many components the package has specified. Basically, ensure
+# minimum 4 components by appending 4 0's and then pick out the first 4.
+# Also take care of the fact that DOTVERSION may have "a" or "b" instead
+# of "." separating the version components.
+DOTSEPARATED=$(DOTVERSION:a=.)
+DOTSEPARATED=$(DOTSEPARATED:b=.)
+!if [echo RCCOMMAVERSION = \> versions.vc] \
+ || [for /f "tokens=1,2,3,4,5* delims=." %a in ("$(DOTSEPARATED).0.0.0.0") do echo %a,%b,%c,%d >> versions.vc]
+!error *** Could not generate RCCOMMAVERSION ***
+!endif
+!include versions.vc
+
+########################################################################
+# 7. Parse the OPTS macro to work out the requested build configuration.
+# Based on this, we will construct the actual switches to be passed to the
+# compiler and linker using the macros defined in the previous section.
+# The following macros are defined by this section based on OPTS
+# STATIC_BUILD - 0 -> Tcl is to be built as a shared library
+# 1 -> build as a static library and shell
+# TCL_THREADS - legacy but always 1 on Windows since winsock requires it.
+# DEBUG - 1 -> debug build, 0 -> release builds
+# SYMBOLS - 1 -> generate PDB's, 0 -> no PDB's
+# PROFILE - 1 -> generate profiling info, 0 -> no profiling
+# PGO - 1 -> profile based optimization, 0 -> no
+# MSVCRT - 1 -> link to dynamic C runtime even when building static Tcl build
+# 0 -> link to static C runtime for static Tcl build.
+# Does not impact shared Tcl builds (STATIC_BUILD == 0)
+# Default: 1 for Tcl 8.7 and up, 0 otherwise.
+# TCL_USE_STATIC_PACKAGES - 1 -> statically link the registry and dde extensions
+# in the Tcl and Wish shell. 0 -> keep them as shared libraries. Does
+# not impact shared Tcl builds. Implied by STATIC_BUILD since Tcl 8.7.
+# USE_THREAD_ALLOC - 1 -> Use a shared global free pool for allocation.
+# 0 -> Use the non-thread allocator.
+# UNCHECKED - 1 -> when doing a debug build with symbols, use the release
+# C runtime, 0 -> use the debug C runtime.
+# USE_STUBS - 1 -> compile to use stubs interfaces, 0 -> direct linking
+# CONFIG_CHECK - 1 -> check current build configuration against Tcl
+# configuration (ignored for Tcl itself)
+# _USE_64BIT_TIME_T - forces a build using 64-bit time_t for 32-bit build
+# (CRT library should support this, not needed for Tcl 9.x)
+# Further, LINKERFLAGS are modified based on above.
+
+# Default values for all the above
STATIC_BUILD = 0
TCL_THREADS = 1
DEBUG = 0
SYMBOLS = 0
PROFILE = 0
PGO = 0
-MSVCRT = 0
-LOIMPACT = 0
+MSVCRT = 1
TCL_USE_STATIC_PACKAGES = 0
USE_THREAD_ALLOC = 1
UNCHECKED = 0
+CONFIG_CHECK = 1
+!if $(DOING_TCL)
+USE_STUBS = 0
!else
+USE_STUBS = 1
+!endif
+
+# If OPTS is not empty AND does not contain "none" which turns off all OPTS
+# set the above macros based on OPTS content
+!if "$(OPTS)" != "" && ![nmakehlp -f "$(OPTS)" "none"]
+
+# OPTS are specified, parse them
+
!if [nmakehlp -f $(OPTS) "static"]
!message *** Doing static
STATIC_BUILD = 1
-!else
-STATIC_BUILD = 0
!endif
+
+!if [nmakehlp -f $(OPTS) "nostubs"]
+!message *** Not using stubs
+USE_STUBS = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "nomsvcrt"]
+!message *** Doing nomsvcrt
+MSVCRT = 0
+!else
!if [nmakehlp -f $(OPTS) "msvcrt"]
!message *** Doing msvcrt
-MSVCRT = 1
!else
+!if $(TCL_MAJOR_VERSION) == 8 && $(TCL_MINOR_VERSION) < 7 && $(STATIC_BUILD)
MSVCRT = 0
!endif
-!if [nmakehlp -f $(OPTS) "staticpkg"]
+!endif
+!endif # [nmakehlp -f $(OPTS) "nomsvcrt"]
+
+!if [nmakehlp -f $(OPTS) "staticpkg"] && $(STATIC_BUILD)
!message *** Doing staticpkg
TCL_USE_STATIC_PACKAGES = 1
-!else
-TCL_USE_STATIC_PACKAGES = 0
!endif
+
!if [nmakehlp -f $(OPTS) "nothreads"]
!message *** Compile explicitly for non-threaded tcl
-TCL_THREADS = 0
-!else
-TCL_THREADS = 1
-USE_THREAD_ALLOC= 1
+TCL_THREADS = 0
+USE_THREAD_ALLOC= 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "tcl8"]
+!message *** Build for Tcl8
+TCL_BUILD_FOR = 8
+!endif
+
+!if $(TCL_MAJOR_VERSION) == 8
+!if [nmakehlp -f $(OPTS) "time64bit"]
+!message *** Force 64-bit time_t
+_USE_64BIT_TIME_T = 1
+!endif
!endif
+
+# Yes, it's weird that the "symbols" option controls DEBUG and
+# the "pdbs" option controls SYMBOLS. That's historical.
!if [nmakehlp -f $(OPTS) "symbols"]
!message *** Doing symbols
DEBUG = 1
!else
DEBUG = 0
!endif
+
!if [nmakehlp -f $(OPTS) "pdbs"]
!message *** Doing pdbs
SYMBOLS = 1
!else
SYMBOLS = 0
!endif
+
!if [nmakehlp -f $(OPTS) "profile"]
!message *** Doing profile
PROFILE = 1
!else
PROFILE = 0
!endif
+
!if [nmakehlp -f $(OPTS) "pgi"]
!message *** Doing profile guided optimization instrumentation
PGO = 1
@@ -276,53 +921,149 @@ PGO = 2
!else
PGO = 0
!endif
+
!if [nmakehlp -f $(OPTS) "loimpact"]
-!message *** Doing loimpact
-LOIMPACT = 1
-!else
-LOIMPACT = 0
+!message *** Warning: ignoring option "loimpact" - deprecated on modern Windows.
!endif
+
+# TBD - should get rid of this option
!if [nmakehlp -f $(OPTS) "thrdalloc"]
!message *** Doing thrdalloc
USE_THREAD_ALLOC = 1
!endif
+
!if [nmakehlp -f $(OPTS) "tclalloc"]
-!message *** Doing tclalloc
USE_THREAD_ALLOC = 0
!endif
-!if [nmakehlp -f $(OPTS) "unchecked"]
-!message *** Doing unchecked
-UNCHECKED = 1
-!else
-UNCHECKED = 0
+
+!if [nmakehlp -f $(OPTS) "unchecked"]
+!message *** Doing unchecked
+UNCHECKED = 1
+!else
+UNCHECKED = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "noconfigcheck"]
+CONFIG_CHECK = 1
+!else
+CONFIG_CHECK = 0
+!endif
+
+!endif # "$(OPTS)" != "" && ... parsing of OPTS
+
+# Set linker flags based on above
+
+!if $(PGO) > 1
+!if [nmakehlp -l -ltcg:pgoptimize $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!elseif $(PGO) > 0
+!if [nmakehlp -l -ltcg:pginstrument $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!endif
+
+################################################################
+# 8. Parse the STATS macro to configure code instrumentation
+# The following macros are set by this section:
+# TCL_MEM_DEBUG - 1 -> enables memory allocation instrumentation
+# 0 -> disables
+# TCL_COMPILE_DEBUG - 1 -> enables byte compiler logging
+# 0 -> disables
+
+# Default both are off
+TCL_MEM_DEBUG = 0
+TCL_COMPILE_DEBUG = 0
+
+!if "$(STATS)" != "" && ![nmakehlp -f "$(STATS)" "none"]
+
+!if [nmakehlp -f $(STATS) "memdbg"]
+!message *** Doing memdbg
+TCL_MEM_DEBUG = 1
+!else
+TCL_MEM_DEBUG = 0
+!endif
+
+!if [nmakehlp -f $(STATS) "compdbg"]
+!message *** Doing compdbg
+TCL_COMPILE_DEBUG = 1
+!else
+TCL_COMPILE_DEBUG = 0
+!endif
+
+!endif
+
+####################################################################
+# 9. Parse the CHECKS macro to configure additional compiler checks
+# The following macros are set by this section:
+# WARNINGS - compiler switches that control the warnings level
+# TCL_NO_DEPRECATED - 1 -> disable support for deprecated functions
+# 0 -> enable deprecated functions
+
+# Defaults - Permit deprecated functions and warning level 3
+TCL_NO_DEPRECATED = 0
+WARNINGS = -W3
+
+!if "$(CHECKS)" != "" && ![nmakehlp -f "$(CHECKS)" "none"]
+
+!if [nmakehlp -f $(CHECKS) "nodep"]
+!message *** Doing nodep check
+TCL_NO_DEPRECATED = 1
+!endif
+
+!if [nmakehlp -f $(CHECKS) "fullwarn"]
+!message *** Doing full warnings check
+WARNINGS = -W4
+!if [nmakehlp -l -warn:3 $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS) -warn:3
!endif
!endif
+!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
+!message *** Doing 64bit portability warnings
+WARNINGS = $(WARNINGS) -Wp64
+!endif
-!if !$(STATIC_BUILD)
-# Make sure we don't build overly fat DLLs.
-MSVCRT = 1
-# We shouldn't statically put the extensions inside the shell when dynamic.
-TCL_USE_STATIC_PACKAGES = 0
!endif
-#----------------------------------------------------------
+################################################################
+# 10. Construct output directory and file paths
# Figure-out how to name our intermediate and output directories.
-# We wouldn't want different builds to use the same .obj files
-# by accident.
-#----------------------------------------------------------
+# In order to avoid inadvertent mixing of object files built using
+# different compilers, build configurations etc.,
+#
+# Naming convention (suffixes):
+# t = full thread support. (Not used for Tcl >= 8.7)
+# s = static library (as opposed to an import library)
+# g = linked to the debug enabled C run-time.
+# x = special static build when it links to the dynamic C run-time.
+#
+# The following macros are set in this section:
+# SUFX - the suffix to use for binaries based on above naming convention
+# BUILDDIRTOP - the toplevel default output directory
+# is of the form {Release,Debug}[_AMD64][_COMPILERVERSION]
+# TMP_DIR - directory where object files are created
+# OUT_DIR - directory where output executables are created
+# Both TMP_DIR and OUT_DIR are defaulted only if not defined by the
+# parent makefile (or command line). The default values are
+# based on BUILDDIRTOP.
+# STUBPREFIX - name of the stubs library for this project
+# PRJIMPLIB - output path of the generated project import library
+# PRJLIBNAME - name of generated project library
+# PRJLIB - output path of generated project library
+# PRJSTUBLIBNAME - name of the generated project stubs library
+# PRJSTUBLIB - output path of the generated project stubs library
+# RESFILE - output resource file (only if not static build)
-#----------------------------------------
-# Naming convention:
-# t = full thread support.
-# s = static library (as opposed to an
-# import library)
-# g = linked to the debug enabled C
-# run-time.
-# x = special static build when it
-# links to the dynamic C run-time.
-#----------------------------------------
SUFX = tsgx
!if $(DEBUG)
@@ -338,7 +1079,7 @@ BUILDDIRTOP =$(BUILDDIRTOP)_$(MACHINE)
BUILDDIRTOP =$(BUILDDIRTOP)_VC$(VCVER)
!endif
-!if !$(DEBUG) || $(DEBUG) && $(UNCHECKED)
+!if !$(DEBUG) || $(TCL_VERSION) > 86 || $(DEBUG) && $(UNCHECKED)
SUFX = $(SUFX:g=)
!endif
@@ -348,20 +1089,18 @@ TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX
TMP_DIRFULL = $(TMP_DIRFULL:Static=)
SUFX = $(SUFX:s=)
EXT = dll
-!if $(MSVCRT)
TMP_DIRFULL = $(TMP_DIRFULL:X=)
SUFX = $(SUFX:x=)
-!endif
!else
TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=)
EXT = lib
-!if !$(MSVCRT)
+!if $(MSVCRT) && $(TCL_VERSION) > 86 || !$(MSVCRT) && $(TCL_VERSION) < 87
TMP_DIRFULL = $(TMP_DIRFULL:X=)
SUFX = $(SUFX:x=)
!endif
!endif
-!if !$(TCL_THREADS)
+!if !$(TCL_THREADS) || $(TCL_VERSION) > 86
TMP_DIRFULL = $(TMP_DIRFULL:Threaded=)
SUFX = $(SUFX:t=)
!endif
@@ -377,335 +1116,798 @@ OUT_DIR = $(TMP_DIR)
!endif
!endif
+# Relative paths -> absolute
+!if [echo OUT_DIR = \> nmakehlp.out] \
+ || [nmakehlp -Q "$(OUT_DIR)" >> nmakehlp.out]
+!error *** Could not fully qualify path OUT_DIR=$(OUT_DIR)
+!endif
+!if [echo TMP_DIR = \>> nmakehlp.out] \
+ || [nmakehlp -Q "$(TMP_DIR)" >> nmakehlp.out]
+!error *** Could not fully qualify path TMP_DIR=$(TMP_DIR)
+!endif
+!include nmakehlp.out
-#----------------------------------------------------------
-# Decode the statistics requested.
-#----------------------------------------------------------
+# The name of the stubs library for the project being built
+STUBPREFIX = $(PROJECT)stub
-!if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"]
-TCL_MEM_DEBUG = 0
-TCL_COMPILE_DEBUG = 0
-!else
-!if [nmakehlp -f $(STATS) "memdbg"]
-!message *** Doing memdbg
-TCL_MEM_DEBUG = 1
+#
+# Set up paths to various Tcl executables and libraries needed by extensions
+#
+
+# TIP 430. Unused for 8.6 but no harm defining it to allow a common rules.vc
+TCL_ZIP_FILE = libtcl$(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)$(TCL_PATCH_LETTER)$(TCL_RELEASE_SERIAL).zip
+TK_ZIP_FILE = libtk$(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)$(TK_PATCH_LETTER)$(TK_RELEASE_SERIAL).zip
+
+!if $(DOING_TCL)
+TCLSHNAME = $(PROJECT)sh$(VERSION)$(SUFX).exe
+TCLSH = $(OUT_DIR)\$(TCLSHNAME)
+TCLIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+TCLLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+TCLLIB = $(OUT_DIR)\$(TCLLIBNAME)
+TCLSCRIPTZIP = $(OUT_DIR)\$(TCL_ZIP_FILE)
+
+!if $(TCL_MAJOR_VERSION) == 8
+TCLSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
!else
-TCL_MEM_DEBUG = 0
+TCLSTUBLIBNAME = $(STUBPREFIX).lib
!endif
-!if [nmakehlp -f $(STATS) "compdbg"]
-!message *** Doing compdbg
-TCL_COMPILE_DEBUG = 1
+TCLSTUBLIB = $(OUT_DIR)\$(TCLSTUBLIBNAME)
+TCL_INCLUDES = -I"$(WIN_DIR)" -I"$(GENERICDIR)"
+
+!else # !$(DOING_TCL)
+
+!if $(TCLINSTALL) # Building against an installed Tcl
+
+# When building extensions, we need to locate tclsh. Depending on version
+# of Tcl we are building against, this may or may not have a "t" suffix.
+# Try various possibilities in turn.
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX:t=).exe
+!if !exist("$(TCLSH)")
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX:t=).exe
+!endif
+
+!if $(TCL_MAJOR_VERSION) == 8
+TCLSTUBLIB = $(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib
!else
-TCL_COMPILE_DEBUG = 0
+TCLSTUBLIB = $(_TCLDIR)\lib\tclstub.lib
!endif
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX:t=).lib
+# When building extensions, may be linking against Tcl that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TCLIMPLIB)")
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)t$(SUFX:t=).lib
!endif
+TCL_LIBRARY = $(_TCLDIR)\lib
+TCLREGLIB = $(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib
+TCLDDELIB = $(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib
+TCLSCRIPTZIP = $(_TCLDIR)\lib\$(TCL_ZIP_FILE)
+TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target
+TCL_INCLUDES = -I"$(_TCLDIR)\include"
+!else # Building against Tcl sources
-#----------------------------------------------------------
-# Decode the checks requested.
-#----------------------------------------------------------
-
-!if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"]
-TCL_NO_DEPRECATED = 0
-WARNINGS = -W3
-!else
-!if [nmakehlp -f $(CHECKS) "nodep"]
-!message *** Doing nodep check
-TCL_NO_DEPRECATED = 1
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX:t=).exe
+!if !exist($(TCLSH))
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX:t=).exe
+!endif
+!if $(TCL_MAJOR_VERSION) == 8
+TCLSTUBLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib
!else
-TCL_NO_DEPRECATED = 0
+TCLSTUBLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub.lib
!endif
-!if [nmakehlp -f $(CHECKS) "fullwarn"]
-!message *** Doing full warnings check
-WARNINGS = -W4
-!if [nmakehlp -l -warn:3]
-LINKERFLAGS = $(LINKERFLAGS) -warn:3
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX:t=).lib
+# When building extensions, may be linking against Tcl that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TCLIMPLIB)")
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)t$(SUFX:t=).lib
!endif
+TCL_LIBRARY = $(_TCLDIR)\library
+TCLREGLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib
+TCLDDELIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib
+TCLSCRIPTZIP = $(_TCLDIR)\win\$(BUILDDIRTOP)\$(TCL_ZIP_FILE)
+TCLTOOLSDIR = $(_TCLDIR)\tools
+TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
+
+!endif # TCLINSTALL
+
+!if !$(STATIC_BUILD) && "$(TCL_BUILD_FOR)" == "8"
+tcllibs = "$(TCLSTUBLIB)"
!else
-WARNINGS = -W3
+tcllibs = "$(TCLSTUBLIB)" "$(TCLIMPLIB)"
!endif
-!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
-!message *** Doing 64bit portability warnings
-WARNINGS = $(WARNINGS) -Wp64
+
+!endif # $(DOING_TCL)
+
+# We need a tclsh that will run on the host machine as part of the build.
+# IX86 runs on all architectures.
+!ifndef TCLSH_NATIVE
+!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
+TCLSH_NATIVE = $(TCLSH)
+!else
+!error You must explicitly set TCLSH_NATIVE for cross-compilation
!endif
!endif
-!if $(PGO) > 1
-!if [nmakehlp -l -ltcg:pgoptimize]
-LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
+# Do the same for Tk and Tk extensions that require the Tk libraries
+!if $(DOING_TK) || $(NEED_TK)
+WISHNAMEPREFIX = wish
+WISHNAME = $(WISHNAMEPREFIX)$(TK_VERSION)$(SUFX).exe
+TKLIBNAME8 = tk$(TK_VERSION)$(SUFX).$(EXT)
+TKLIBNAME9 = tcl9tk$(TK_VERSION)$(SUFX).$(EXT)
+!if $(TCL_MAJOR_VERSION) == 8 || "$(TCL_BUILD_FOR)" == "8"
+TKLIBNAME = tk$(TK_VERSION)$(SUFX).$(EXT)
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX).lib
!else
-MSG=^
-This compiler does not support profile guided optimization.
-!error $(MSG)
+TKLIBNAME = tcl9tk$(TK_VERSION)$(SUFX).$(EXT)
+TKIMPLIBNAME = tcl9tk$(TK_VERSION)$(SUFX).lib
!endif
-!elseif $(PGO) > 0
-!if [nmakehlp -l -ltcg:pginstrument]
-LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
+!if $(TK_MAJOR_VERSION) == 8
+TKSTUBLIBNAME = tkstub$(TK_VERSION).lib
!else
-MSG=^
-This compiler does not support profile guided optimization.
-!error $(MSG)
+TKSTUBLIBNAME = tkstub.lib
!endif
+
+!if $(DOING_TK)
+WISH = $(OUT_DIR)\$(WISHNAME)
+TKSTUBLIB = $(OUT_DIR)\$(TKSTUBLIBNAME)
+TKIMPLIB = $(OUT_DIR)\$(TKIMPLIBNAME)
+TKLIB = $(OUT_DIR)\$(TKLIBNAME)
+TK_INCLUDES = -I"$(WIN_DIR)" -I"$(GENERICDIR)"
+TKSCRIPTZIP = $(OUT_DIR)\$(TK_ZIP_FILE)
+
+!else # effectively NEED_TK
+
+!if $(TKINSTALL) # Building against installed Tk
+WISH = $(_TKDIR)\bin\$(WISHNAME)
+TKSTUBLIB = $(_TKDIR)\lib\$(TKSTUBLIBNAME)
+TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME)
+# When building extensions, may be linking against Tk that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TKIMPLIB)")
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib
+TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME)
!endif
+TK_INCLUDES = -I"$(_TKDIR)\include"
+TKSCRIPTZIP = $(_TKDIR)\lib\$(TK_ZIP_FILE)
-#----------------------------------------------------------
-# Set our defines now armed with our options.
-#----------------------------------------------------------
+!else # Building against Tk sources
+
+WISH = $(_TKDIR)\win\$(BUILDDIRTOP)\$(WISHNAME)
+TKSTUBLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKSTUBLIBNAME)
+TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
+# When building extensions, may be linking against Tk that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TKIMPLIB)")
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib
+TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
+!endif
+TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+TKSCRIPTZIP = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TK_ZIP_FILE)
+
+!endif # TKINSTALL
+
+tklibs = "$(TKSTUBLIB)" "$(TKIMPLIB)"
+
+!endif # $(DOING_TK)
+!endif # $(DOING_TK) || $(NEED_TK)
+
+# Various output paths
+PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+PRJLIBNAME8 = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+# Even when building against Tcl 8, PRJLIBNAME9 must not have "t"
+PRJLIBNAME9 = tcl9$(PROJECT)$(VERSION)$(SUFX:t=).$(EXT)
+!if $(TCL_MAJOR_VERSION) == 8 || "$(TCL_BUILD_FOR)" == "8"
+PRJLIBNAME = $(PRJLIBNAME8)
+!else
+PRJLIBNAME = $(PRJLIBNAME9)
+!endif
+PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
+
+!if $(TCL_MAJOR_VERSION) == 8
+PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
+!else
+PRJSTUBLIBNAME = $(STUBPREFIX).lib
+!endif
+PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME)
-OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS
+# If extension parent makefile has not defined a resource definition file,
+# we will generate one from standard template.
+!if !$(DOING_TCL) && !$(DOING_TK) && !$(STATIC_BUILD)
+!ifdef RCFILE
+RESFILE = $(TMP_DIR)\$(RCFILE:.rc=.res)
+!else
+RESFILE = $(TMP_DIR)\$(PROJECT).res
+!endif
+!endif
+
+###################################################################
+# 11. Construct the paths for the installation directories
+# The following macros get defined in this section:
+# LIB_INSTALL_DIR - where libraries should be installed
+# BIN_INSTALL_DIR - where the executables should be installed
+# DOC_INSTALL_DIR - where documentation should be installed
+# SCRIPT_INSTALL_DIR - where scripts should be installed
+# INCLUDE_INSTALL_DIR - where C include files should be installed
+# DEMO_INSTALL_DIR - where demos should be installed
+# PRJ_INSTALL_DIR - where package will be installed (not set for Tcl and Tk)
+
+!if $(DOING_TCL) || $(DOING_TK)
+LIB_INSTALL_DIR = $(_INSTALLDIR)\lib
+BIN_INSTALL_DIR = $(_INSTALLDIR)\bin
+DOC_INSTALL_DIR = $(_INSTALLDIR)\doc
+!if $(DOING_TCL)
+SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+MODULE_INSTALL_DIR = $(_INSTALLDIR)\lib\tcl$(TCL_MAJOR_VERSION)
+!else # DOING_TK
+SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+!endif
+DEMO_INSTALL_DIR = $(SCRIPT_INSTALL_DIR)\demos
+INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\include
+
+!else # extension other than Tk
+
+PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
+!if $(MULTIPLATFORM_INSTALL)
+LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR)\$(PLATFORM_IDENTIFY)
+BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR)\$(PLATFORM_IDENTIFY)
+!else
+LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+!endif
+DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+DEMO_INSTALL_DIR = $(PRJ_INSTALL_DIR)\demos
+INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\..\include
+
+!endif
+
+###################################################################
+# 12. Set up actual options to be passed to the compiler and linker
+# Now we have all the information we need, set up the actual flags and
+# options that we will pass to the compiler and linker. The main
+# makefile should use these in combination with whatever other flags
+# and switches are specific to it.
+# The following macros are defined, names are for historical compatibility:
+# OPTDEFINES - /Dxxx C macro flags based on user-specified OPTS
+# COMPILERFLAGS - /Dxxx C macro flags independent of any configuration options
+# crt - Compiler switch that selects the appropriate C runtime
+# cdebug - Compiler switches related to debug AND optimizations
+# cwarn - Compiler switches that set warning levels
+# cflags - complete compiler switches (subsumes cdebug and cwarn)
+# ldebug - Linker switches controlling debug information and optimization
+# lflags - complete linker switches (subsumes ldebug) except subsystem type
+# dlllflags - complete linker switches to build DLLs (subsumes lflags)
+# conlflags - complete linker switches for console program (subsumes lflags)
+# guilflags - complete linker switches for GUI program (subsumes lflags)
+# baselibs - minimum Windows libraries required. Parent makefile can
+# define PRJ_LIBS before including rules.rc if additional libs are needed
+
+OPTDEFINES = /DSTDC_HEADERS /DUSE_NMAKE=1
+!if $(VCVERSION) > 1600
+OPTDEFINES = $(OPTDEFINES) /DHAVE_STDINT_H=1
+!else
+OPTDEFINES = $(OPTDEFINES) /DMP_NO_STDINT=1
+!endif
+!if $(VCVERSION) >= 1800
+OPTDEFINES = $(OPTDEFINES) /DHAVE_INTTYPES_H=1 /DHAVE_STDBOOL_H=1
+!endif
!if $(TCL_MEM_DEBUG)
-OPTDEFINES = $(OPTDEFINES) -DTCL_MEM_DEBUG
+OPTDEFINES = $(OPTDEFINES) /DTCL_MEM_DEBUG
!endif
!if $(TCL_COMPILE_DEBUG)
-OPTDEFINES = $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
+OPTDEFINES = $(OPTDEFINES) /DTCL_COMPILE_DEBUG /DTCL_COMPILE_STATS
!endif
-!if $(TCL_THREADS)
-OPTDEFINES = $(OPTDEFINES) -DTCL_THREADS=1
-!if $(USE_THREAD_ALLOC)
-OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_ALLOC=1
+!if $(TCL_THREADS) && $(TCL_VERSION) < 87
+OPTDEFINES = $(OPTDEFINES) /DTCL_THREADS=1
+!if $(USE_THREAD_ALLOC) && $(TCL_VERSION) < 87
+OPTDEFINES = $(OPTDEFINES) /DUSE_THREAD_ALLOC=1
!endif
!endif
!if $(STATIC_BUILD)
-OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD
+OPTDEFINES = $(OPTDEFINES) /DSTATIC_BUILD
+!elseif $(TCL_VERSION) > 86
+OPTDEFINES = $(OPTDEFINES) /DTCL_WITH_EXTERNAL_TOMMATH
+!if "$(MACHINE)" == "AMD64" || "$(MACHINE)" == "ARM64"
+OPTDEFINES = $(OPTDEFINES) /DMP_64BIT
+!endif
!endif
!if $(TCL_NO_DEPRECATED)
-OPTDEFINES = $(OPTDEFINES) -DTCL_NO_DEPRECATED
+OPTDEFINES = $(OPTDEFINES) /DTCL_NO_DEPRECATED
+!endif
+
+!if $(USE_STUBS)
+# Note we do not define USE_TCL_STUBS even when building tk since some
+# test targets in tk do not use stubs
+!if !$(DOING_TCL)
+USE_STUBS_DEFS = /DUSE_TCL_STUBS /DUSE_TCLOO_STUBS
+!if $(NEED_TK)
+USE_STUBS_DEFS = $(USE_STUBS_DEFS) /DUSE_TK_STUBS
+!endif
!endif
+!endif # USE_STUBS
!if !$(DEBUG)
-OPTDEFINES = $(OPTDEFINES) -DNDEBUG
+OPTDEFINES = $(OPTDEFINES) /DNDEBUG
!if $(OPTIMIZING)
-OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED
+OPTDEFINES = $(OPTDEFINES) /DTCL_CFG_OPTIMIZED
!endif
!endif
!if $(PROFILE)
-OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED
+OPTDEFINES = $(OPTDEFINES) /DTCL_CFG_PROFILED
!endif
-!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
-OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT
+!if "$(MACHINE)" == "AMD64" || "$(MACHINE)" == "ARM64"
+OPTDEFINES = $(OPTDEFINES) /DTCL_CFG_DO64BIT
!endif
!if $(VCVERSION) < 1300
-OPTDEFINES = $(OPTDEFINES) -DNO_STRTOI64
+OPTDEFINES = $(OPTDEFINES) /DNO_STRTOI64=1
!endif
-#----------------------------------------------------------
-# Locate the Tcl headers to build against
-#----------------------------------------------------------
-
-!if "$(PROJECT)" == "tcl"
-
-_TCL_H = ..\generic\tcl.h
-
-!else
+!if $(TCL_MAJOR_VERSION) == 8
+!if "$(_USE_64BIT_TIME_T)" == "1"
+OPTDEFINES = $(OPTDEFINES) /D_USE_64BIT_TIME_T=1
+!endif
+!endif
+!if "$(TCL_BUILD_FOR)" == "8"
+OPTDEFINES = $(OPTDEFINES) /DTCL_MAJOR_VERSION=8
+!endif
-# If INSTALLDIR set to tcl root dir then reset to the lib dir.
-!if exist("$(_INSTALLDIR)\include\tcl.h")
-_INSTALLDIR=$(_INSTALLDIR)\lib
+# Like the TEA system only set this non empty for non-Tk extensions
+# Note: some extensions use PACKAGE_NAME and others use PACKAGE_TCLNAME
+# so we pass both
+!if !$(DOING_TCL) && !$(DOING_TK)
+PKGNAMEFLAGS = /DPACKAGE_NAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
+ /DPACKAGE_TCLNAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
+ /DPACKAGE_VERSION="\"$(DOTVERSION)\"" \
+ /DMODULE_SCOPE=extern
!endif
-!if !defined(TCLDIR)
-!if exist("$(_INSTALLDIR)\..\include\tcl.h")
-TCLINSTALL = 1
-_TCLDIR = $(_INSTALLDIR)\..
-_TCL_H = $(_INSTALLDIR)\..\include\tcl.h
-TCLDIR = $(_INSTALLDIR)\..
+# crt picks the C run time based on selected OPTS
+!if $(MSVCRT)
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MDd
!else
-MSG=^
-Failed to find tcl.h. Set the TCLDIR macro.
-!error $(MSG)
+crt = -MD
!endif
!else
-_TCLDIR = $(TCLDIR:/=\)
-!if exist("$(_TCLDIR)\include\tcl.h")
-TCLINSTALL = 1
-_TCL_H = $(_TCLDIR)\include\tcl.h
-!elseif exist("$(_TCLDIR)\generic\tcl.h")
-TCLINSTALL = 0
-_TCL_H = $(_TCLDIR)\generic\tcl.h
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MTd
!else
-MSG =^
-Failed to find tcl.h. The TCLDIR macro does not appear correct.
-!error $(MSG)
-!endif
+crt = -MT
!endif
!endif
-#--------------------------------------------------------------
-# Extract various version numbers from tcl headers
-# The generated file is then included in the makefile.
-#--------------------------------------------------------------
+# cdebug includes compiler options for debugging as well as optimization.
+!if $(DEBUG)
-!if [echo REM = This file is generated from rules.vc > versions.vc]
-!endif
-!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc]
-!endif
-!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
-!endif
-!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
- && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
-!endif
+# In debugging mode, optimizations need to be disabled
+cdebug = -Zi -Od $(DEBUGFLAGS)
-# If building the tcl core then we need additional package versions
-!if "$(PROJECT)" == "tcl"
-!if [echo PKG_HTTP_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\http\pkgIndex.tcl http >> versions.vc]
-!endif
-!if [echo PKG_TCLTEST_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\tcltest\pkgIndex.tcl tcltest >> versions.vc]
+!else
+
+cdebug = $(OPTIMIZATIONS)
+!if $(SYMBOLS)
+cdebug = $(cdebug) -Zi
!endif
-!if [echo PKG_MSGCAT_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\msgcat\pkgIndex.tcl msgcat >> versions.vc]
+
+!endif # $(DEBUG)
+
+# cwarn includes default warning levels, also C4090 (buggy) and C4146 is useless.
+cwarn = $(WARNINGS) -wd4090 -wd4146
+
+!if "$(MACHINE)" == "AMD64" || "$(MACHINE)" == "ARM64"
+# Disable pointer<->int warnings related to cast between different sizes
+# There are a gadzillion of these due to use of ClientData and
+# clutter up compiler
+# output increasing chance of a real warning getting lost. So disable them.
+# Eventually some day, Tcl will be 64-bit clean.
+cwarn = $(cwarn) -wd4311 -wd4312
!endif
-!if [echo PKG_PLATFORM_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform " >> versions.vc]
+
+### Common compiler options that are architecture specific
+!if "$(MACHINE)" == "ARM"
+carch = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
+!else
+carch =
!endif
-!if [echo PKG_SHELL_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform::shell" >> versions.vc]
+
+# cpuid is only available on intel machines
+!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "AMD64"
+carch = $(carch) /DHAVE_CPUID=1
!endif
-!if [echo PKG_DDE_VER = \>> versions.vc] \
- && [nmakehlp -V ..\library\dde\pkgIndex.tcl "dde " >> versions.vc]
+
+!if $(DEBUG)
+# Turn warnings into errors
+cwarn = $(cwarn) -WX
!endif
-!if [echo PKG_REG_VER =\>> versions.vc] \
- && [nmakehlp -V ..\library\reg\pkgIndex.tcl registry >> versions.vc]
+
+INCLUDES = $(TCL_INCLUDES) $(TK_INCLUDES) $(PRJ_INCLUDES)
+!if !$(DOING_TCL) && !$(DOING_TK)
+INCLUDES = $(INCLUDES) -I"$(GENERICDIR)" -I"$(WIN_DIR)" -I"$(COMPATDIR)"
!endif
+
+# These flags are defined roughly in the order of the pre-reform
+# rules.vc/makefile.vc to help visually compare that the pre- and
+# post-reform build logs
+
+# cflags contains generic flags used for building practically all object files
+cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\ $(cdebug)
+
+!if $(TCL_MAJOR_VERSION) == 8 && $(TCL_MINOR_VERSION) < 7
+cflags = $(cflags) -DTcl_Size=int
!endif
-!include versions.vc
+# appcflags contains $(cflags) and flags for building the application
+# object files (e.g. tclsh, or wish) pkgcflags contains $(cflags) plus
+# flags used for building shared object files The two differ in the
+# BUILD_$(PROJECT) macro which should be defined only for the shared
+# library *implementation* and not for its caller interface
-#--------------------------------------------------------------
-# Setup tcl version dependent stuff headers
-#--------------------------------------------------------------
+appcflags_nostubs = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES)
+appcflags = $(appcflags_nostubs) $(USE_STUBS_DEFS)
+pkgcflags = $(appcflags) $(PKGNAMEFLAGS) /DBUILD_$(PROJECT)
+pkgcflags_nostubs = $(appcflags_nostubs) $(PKGNAMEFLAGS) /DBUILD_$(PROJECT)
-!if "$(PROJECT)" != "tcl"
+# stubscflags contains $(cflags) plus flags used for building a stubs
+# library for the package. Note: /DSTATIC_BUILD is defined in
+# $(OPTDEFINES) only if the OPTS configuration indicates a static
+# library. However the stubs library is ALWAYS static hence included
+# here irrespective of the OPTS setting.
+#
+# TBD - tclvfs has a comment that stubs libs should not be compiled with -GL
+# without stating why. Tcl itself compiled stubs libs with this flag.
+# so we do not remove it from cflags. -GL may prevent extensions
+# compiled with one VC version to fail to link against stubs library
+# compiled with another VC version. Check for this and fix accordingly.
+stubscflags = $(cflags) $(PKGNAMEFLAGS) $(PRJ_DEFINES) $(OPTDEFINES) /Zl /GL- /DSTATIC_BUILD $(INCLUDES) $(USE_STUBS_DEFS)
-TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+# Link flags
-!if $(TCL_VERSION) < 81
-TCL_DOES_STUBS = 0
+!if $(DEBUG)
+ldebug = -debug -debugtype:cv
!else
-TCL_DOES_STUBS = 1
+ldebug = -release -opt:ref -opt:icf,3
+!if $(SYMBOLS)
+ldebug = $(ldebug) -debug -debugtype:cv
+!endif
!endif
-!if $(TCLINSTALL)
-TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe"
-!if !exist($(TCLSH)) && $(TCL_THREADS)
-TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe"
+# Note: Profiling is currently only possible with the Visual Studio Enterprise
+!if $(PROFILE)
+ldebug= $(ldebug) -profile
!endif
-TCLSTUBLIB = "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib"
-TCLIMPLIB = "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib"
-TCL_LIBRARY = $(_TCLDIR)\lib
-TCLREGLIB = "$(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib"
-TCLDDELIB = "$(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib"
-COFFBASE = \must\have\tcl\sources\to\build\this\target
-TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target
-TCL_INCLUDES = -I"$(_TCLDIR)\include"
-!else
-TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe"
-!if !exist($(TCLSH)) && $(TCL_THREADS)
-TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe"
+
+### Declarations common to all linker versions
+lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)
+
+!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
+lflags = $(lflags) -nodefaultlib:libucrt.lib
!endif
-TCLSTUBLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib"
-TCLIMPLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib"
-TCL_LIBRARY = $(_TCLDIR)\library
-TCLREGLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib"
-TCLDDELIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib"
-COFFBASE = "$(_TCLDIR)\win\coffbase.txt"
-TCLTOOLSDIR = $(_TCLDIR)\tools
-TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
+
+dlllflags = $(lflags) -dll
+conlflags = $(lflags) -subsystem:console
+guilflags = $(lflags) -subsystem:windows
+
+# Libraries that are required for every image.
+# Extensions should define any additional libraries with $(PRJ_LIBS)
+winlibs = kernel32.lib advapi32.lib
+
+!if $(NEED_TK)
+winlibs = $(winlibs) gdi32.lib user32.lib uxtheme.lib
!endif
+# Avoid 'unresolved external symbol __security_cookie' errors.
+# c.f. http://support.microsoft.com/?id=894573
+!if "$(MACHINE)" == "AMD64"
+!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500
+winlibs = $(winlibs) bufferoverflowU.lib
+!endif
!endif
-#-------------------------------------------------------------------------
-# Locate the Tk headers to build against
-#-------------------------------------------------------------------------
+baselibs = $(winlibs) $(PRJ_LIBS)
-!if "$(PROJECT)" == "tk"
-_TK_H = ..\generic\tk.h
-_INSTALLDIR = $(_INSTALLDIR)\..
+!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
+baselibs = $(baselibs) ucrt.lib
!endif
-!ifdef PROJECT_REQUIRES_TK
-!if !defined(TKDIR)
-!if exist("$(_INSTALLDIR)\..\include\tk.h")
-TKINSTALL = 1
-_TKDIR = $(_INSTALLDIR)\..
-_TK_H = $(_TKDIR)\include\tk.h
-TKDIR = $(_TKDIR)
-!elseif exist("$(_TCLDIR)\include\tk.h")
-TKINSTALL = 1
-_TKDIR = $(_TCLDIR)
-_TK_H = $(_TKDIR)\include\tk.h
-TKDIR = $(_TKDIR)
+################################################################
+# 13. Define standard commands, common make targets and implicit rules
+
+CCPKGCMD = $(cc32) $(pkgcflags) -Fo$(TMP_DIR)^\
+CCAPPCMD = $(cc32) $(appcflags) -Fo$(TMP_DIR)^\
+CCSTUBSCMD = $(cc32) $(stubscflags) -Fo$(TMP_DIR)^\
+
+LIBCMD = $(lib32) -nologo $(LINKERFLAGS) -out:$@
+DLLCMD = $(link32) $(dlllflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+
+CONEXECMD = $(link32) $(conlflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+GUIEXECMD = $(link32) $(guilflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+RESCMD = $(rc32) -fo $@ -r -i "$(GENERICDIR)" -i "$(TMP_DIR)" \
+ $(TCL_INCLUDES) /DSTATIC_BUILD=$(STATIC_BUILD) \
+ /DDEBUG=$(DEBUG) -d UNCHECKED=$(UNCHECKED) \
+ /DCOMMAVERSION=$(RCCOMMAVERSION) \
+ /DDOTVERSION=\"$(DOTVERSION)\" \
+ /DVERSION=\"$(VERSION)\" \
+ /DSUFX=\"$(SUFX)\" \
+ /DPROJECT=\"$(PROJECT)\" \
+ /DPRJLIBNAME=\"$(PRJLIBNAME)\"
+
+!ifndef DEFAULT_BUILD_TARGET
+DEFAULT_BUILD_TARGET = $(PROJECT)
!endif
+
+default-target: $(DEFAULT_BUILD_TARGET)
+
+!if $(MULTIPLATFORM_INSTALL)
+default-pkgindex:
+ @echo if {[package vsatisfies [package provide Tcl] 9.0-]} { > $(OUT_DIR)\pkgIndex.tcl
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PLATFORM_IDENTIFY) $(PRJLIBNAME9)]] >> $(OUT_DIR)\pkgIndex.tcl
+ @echo } else { >> $(OUT_DIR)\pkgIndex.tcl
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PLATFORM_IDENTIFY) $(PRJLIBNAME8)]] >> $(OUT_DIR)\pkgIndex.tcl
+ @echo } >> $(OUT_DIR)\pkgIndex.tcl
!else
-_TKDIR = $(TKDIR:/=\)
-!if exist("$(_TKDIR)\include\tk.h")
-TKINSTALL = 1
-_TK_H = $(_TKDIR)\include\tk.h
-!elseif exist("$(_TKDIR)\generic\tk.h")
-TKINSTALL = 0
-_TK_H = $(_TKDIR)\generic\tk.h
+default-pkgindex:
+ @echo if {[package vsatisfies [package provide Tcl] 9.0-]} { > $(OUT_DIR)\pkgIndex.tcl
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME9)]] >> $(OUT_DIR)\pkgIndex.tcl
+ @echo } else { >> $(OUT_DIR)\pkgIndex.tcl
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME8)]] >> $(OUT_DIR)\pkgIndex.tcl
+ @echo } >> $(OUT_DIR)\pkgIndex.tcl
+!endif
+
+default-pkgindex-tea:
+ @if exist $(ROOT)\pkgIndex.tcl.in nmakehlp -s << $(ROOT)\pkgIndex.tcl.in > $(OUT_DIR)\pkgIndex.tcl
+@PACKAGE_VERSION@ $(DOTVERSION)
+@PACKAGE_NAME@ $(PRJ_PACKAGE_TCLNAME)
+@PACKAGE_TCLNAME@ $(PRJ_PACKAGE_TCLNAME)
+@PKG_LIB_FILE@ $(PRJLIBNAME)
+@PKG_LIB_FILE8@ $(PRJLIBNAME8)
+@PKG_LIB_FILE9@ $(PRJLIBNAME9)
+<<
+
+default-install: default-install-binaries default-install-libraries
+!if $(SYMBOLS)
+default-install: default-install-pdbs
+!endif
+
+# Again to deal with historical brokenness, there is some confusion
+# in terminlogy. For extensions, the "install-binaries" was used to
+# locate target directory for *binary shared libraries* and thus
+# the appropriate macro is LIB_INSTALL_DIR since BIN_INSTALL_DIR is
+# for executables (exes). On the other hand the "install-libraries"
+# target is for *scripts* and should have been called "install-scripts".
+default-install-binaries: $(PRJLIB)
+ @echo Installing binaries to '$(LIB_INSTALL_DIR)'
+ @if not exist "$(LIB_INSTALL_DIR)" mkdir "$(LIB_INSTALL_DIR)"
+ @$(CPY) $(PRJLIB) "$(LIB_INSTALL_DIR)" >NUL
+
+# Alias for default-install-scripts
+default-install-libraries: default-install-scripts
+
+default-install-scripts: $(OUT_DIR)\pkgIndex.tcl
+ @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
+ @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
+ @echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
+ @$(CPY) $(OUT_DIR)\pkgIndex.tcl $(SCRIPT_INSTALL_DIR)
+
+default-install-stubs:
+ @echo Installing stubs library to '$(SCRIPT_INSTALL_DIR)'
+ @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+ @$(CPY) $(PRJSTUBLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+default-install-pdbs:
+ @echo Installing PDBs to '$(LIB_INSTALL_DIR)'
+ @if not exist "$(LIB_INSTALL_DIR)" mkdir "$(LIB_INSTALL_DIR)"
+ @$(CPY) "$(OUT_DIR)\*.pdb" "$(LIB_INSTALL_DIR)\"
+
+# "emacs font-lock highlighting fix
+
+default-install-docs-html:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
+ @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.html" "$(DOCDIR)\*.css" "$(DOCDIR)\*.png") do @$(COPY) %f "$(DOC_INSTALL_DIR)"
+
+default-install-docs-n:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
+ @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.n") do @$(COPY) %f "$(DOC_INSTALL_DIR)"
+
+default-install-demos:
+ @echo Installing demos to '$(DEMO_INSTALL_DIR)'
+ @if not exist "$(DEMO_INSTALL_DIR)" mkdir "$(DEMO_INSTALL_DIR)"
+ @if exist $(DEMODIR) $(CPYDIR) "$(DEMODIR)" "$(DEMO_INSTALL_DIR)"
+
+default-clean:
+ @echo Cleaning $(TMP_DIR)\* ...
+ @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
+ @echo Cleaning $(WIN_DIR)\nmakehlp.obj, nmakehlp.exe ...
+ @if exist $(WIN_DIR)\nmakehlp.obj del $(WIN_DIR)\nmakehlp.obj
+ @if exist $(WIN_DIR)\nmakehlp.exe del $(WIN_DIR)\nmakehlp.exe
+ @if exist $(WIN_DIR)\nmakehlp.out del $(WIN_DIR)\nmakehlp.out
+ @echo Cleaning $(WIN_DIR)\nmhlp-out.txt ...
+ @if exist $(WIN_DIR)\nmhlp-out.txt del $(WIN_DIR)\nmhlp-out.txt
+ @echo Cleaning $(WIN_DIR)\_junk.pch ...
+ @if exist $(WIN_DIR)\_junk.pch del $(WIN_DIR)\_junk.pch
+ @echo Cleaning $(WIN_DIR)\vercl.x, vercl.i ...
+ @if exist $(WIN_DIR)\vercl.x del $(WIN_DIR)\vercl.x
+ @if exist $(WIN_DIR)\vercl.i del $(WIN_DIR)\vercl.i
+ @echo Cleaning $(WIN_DIR)\versions.vc, version.vc ...
+ @if exist $(WIN_DIR)\versions.vc del $(WIN_DIR)\versions.vc
+ @if exist $(WIN_DIR)\version.vc del $(WIN_DIR)\version.vc
+
+default-hose: default-clean
+ @echo Hosing $(OUT_DIR)\* ...
+ @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
+
+# Only for backward compatibility
+default-distclean: default-hose
+
+default-setup:
+ @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
+ @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
+
+!if "$(TESTPAT)" != ""
+TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
+!endif
+
+default-test: default-setup $(PROJECT)
+ @set TCLLIBPATH=$(OUT_DIR:\=/)
+ @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
+ cd "$(TESTDIR)" && $(DEBUGGER) $(TCLSH) all.tcl $(TESTFLAGS)
+
+default-shell: default-setup $(PROJECT)
+ @set TCLLIBPATH=$(OUT_DIR:\=/)
+ @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
+ $(DEBUGGER) $(TCLSH)
+
+# Generation of Windows version resource
+!ifdef RCFILE
+
+# Note: don't use $** in below rule because there may be other dependencies
+# and only the "main" rc must be passed to the resource compiler
+$(TMP_DIR)\$(PROJECT).res: $(RCDIR)\$(PROJECT).rc
+ $(RESCMD) $(RCDIR)\$(PROJECT).rc
+
!else
-MSG =^
-Failed to find tk.h. The TKDIR macro does not appear correct.
-!error $(MSG)
-!endif
-!endif
+
+# If parent makefile has not defined a resource definition file,
+# we will generate one from standard template.
+$(TMP_DIR)\$(PROJECT).res: $(TMP_DIR)\$(PROJECT).rc
+
+$(TMP_DIR)\$(PROJECT).rc:
+ @$(COPY) << $(TMP_DIR)\$(PROJECT).rc
+#include
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION COMMAVERSION
+ PRODUCTVERSION COMMAVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "Tcl extension " PROJECT
+ VALUE "OriginalFilename", PRJLIBNAME
+ VALUE "FileVersion", DOTVERSION
+ VALUE "ProductName", "Package " PROJECT " for Tcl"
+ VALUE "ProductVersion", DOTVERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+<<
+
+!endif # ifdef RCFILE
+
+!ifndef DISABLE_IMPLICIT_RULES
+DISABLE_IMPLICIT_RULES = 0
!endif
-#-------------------------------------------------------------------------
-# Extract Tk version numbers
-#-------------------------------------------------------------------------
+!if !$(DISABLE_IMPLICIT_RULES)
+# Implicit rule definitions - only for building library objects. For stubs and
+# main application, the makefile should define explicit rules.
-!if defined(PROJECT_REQUIRES_TK) || "$(PROJECT)" == "tk"
+{$(ROOT)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(WIN_DIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(RCDIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+{$(WIN_DIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+{$(TMP_DIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+.SUFFIXES:
+.SUFFIXES:.c .rc
-!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc]
!endif
-!if [echo TK_MINOR_VERSION = \>> versions.vc] \
- && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
+
+################################################################
+# 14. Sanity check selected options against Tcl build options
+# When building an extension, certain configuration options should
+# match the ones used when Tcl was built. Here we check and
+# warn on a mismatch.
+!if !$(DOING_TCL)
+
+!if $(TCLINSTALL) # Building against an installed Tcl
+!if exist("$(_TCLDIR)\lib\nmake\tcl.nmake")
+TCLNMAKECONFIG = "$(_TCLDIR)\lib\nmake\tcl.nmake"
!endif
-!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
- && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
+!else # !$(TCLINSTALL) - building against Tcl source
+!if exist("$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl.nmake")
+TCLNMAKECONFIG = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl.nmake"
!endif
+!endif # TCLINSTALL
-!include versions.vc
-
-TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
-TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
+!if $(CONFIG_CHECK)
+!ifdef TCLNMAKECONFIG
+!include $(TCLNMAKECONFIG)
-!if "$(PROJECT)" != "tk"
-!if $(TKINSTALL)
-WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe"
-TKSTUBLIB = "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib"
-TKIMPLIB = "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib"
-TK_INCLUDES = -I"$(_TKDIR)\include"
-!else
-WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe"
-TKSTUBLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib"
-TKIMPLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib"
-TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+!if defined(CORE_MACHINE) && "$(CORE_MACHINE)" != "$(MACHINE)"
+!error ERROR: Build target ($(MACHINE)) does not match the Tcl library architecture ($(CORE_MACHINE)).
!endif
+!if $(TCL_VERSION) < 87 && defined(CORE_USE_THREAD_ALLOC) && $(CORE_USE_THREAD_ALLOC) != $(USE_THREAD_ALLOC)
+!message WARNING: Value of USE_THREAD_ALLOC ($(USE_THREAD_ALLOC)) does not match its Tcl core value ($(CORE_USE_THREAD_ALLOC)).
!endif
-
+!if defined(CORE_DEBUG) && $(CORE_DEBUG) != $(DEBUG)
+!message WARNING: Value of DEBUG ($(DEBUG)) does not match its Tcl library configuration ($(DEBUG)).
!endif
+!endif
+
+!endif # TCLNMAKECONFIG
+
+!endif # !$(DOING_TCL)
+
#----------------------------------------------------------
# Display stats being used.
#----------------------------------------------------------
+!if !$(DOING_TCL)
+!message *** Building against Tcl at '$(_TCLDIR)'
+!endif
+!if !$(DOING_TK) && $(NEED_TK)
+!message *** Building against Tk at '$(_TKDIR)'
+!endif
!message *** Intermediate directory will be '$(TMP_DIR)'
!message *** Output directory will be '$(OUT_DIR)'
+!message *** Installation, if selected, will be in '$(_INSTALLDIR)'
!message *** Suffix for binaries will be '$(SUFX)'
-!message *** Optional defines are '$(OPTDEFINES)'
-!message *** Compiler version $(VCVER). Target machine is $(MACHINE)
-!message *** Host architecture is $(NATIVE_ARCH)
-!message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)'
-!message *** Link options '$(LINKERFLAGS)'
-
-!endif
+!message *** Compiler version $(VCVER). Target $(MACHINE), host $(NATIVE_ARCH).
+!endif # ifdef _RULES_VC
diff --git a/sqlite/autoconf/tea/win/targets.vc b/sqlite/autoconf/tea/win/targets.vc
new file mode 100644
index 00000000..49ed7d4a
--- /dev/null
+++ b/sqlite/autoconf/tea/win/targets.vc
@@ -0,0 +1,98 @@
+#------------------------------------------------------------- -*- makefile -*-
+# targets.vc --
+#
+# Part of the nmake based build system for Tcl and its extensions.
+# This file defines some standard targets for the convenience of extensions
+# and can be optionally included by the extension makefile.
+# See TIP 477 (https://core.tcl-lang.org/tips/doc/main/tip/477.md) for docs.
+
+$(PROJECT): setup pkgindex $(PRJLIB)
+
+!ifdef PRJ_STUBOBJS
+$(PROJECT): $(PRJSTUBLIB)
+$(PRJSTUBLIB): $(PRJ_STUBOBJS)
+ $(LIBCMD) $**
+
+$(PRJ_STUBOBJS):
+ $(CCSTUBSCMD) %s
+!endif # PRJ_STUBOBJS
+
+!ifdef PRJ_MANIFEST
+$(PROJECT): $(PRJLIB).manifest
+$(PRJLIB).manifest: $(PRJ_MANIFEST)
+ @nmakehlp -s << $** >$@
+@MACHINE@ $(MACHINE:IX86=X86)
+<<
+!endif
+
+!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk"
+$(PRJLIB): $(PRJ_OBJS) $(RESFILE)
+!if $(STATIC_BUILD)
+ $(LIBCMD) $**
+!else
+ $(DLLCMD) $**
+ $(_VC_MANIFEST_EMBED_DLL)
+!endif
+ -@del $*.exp
+!endif
+
+!if "$(PRJ_HEADERS)" != "" && "$(PRJ_OBJS)" != ""
+$(PRJ_OBJS): $(PRJ_HEADERS)
+!endif
+
+# If parent makefile has defined stub objects, add their installation
+# to the default install
+!if "$(PRJ_STUBOBJS)" != ""
+default-install: default-install-stubs
+!endif
+
+# Unlike the other default targets, these cannot be in rules.vc because
+# the executed command depends on existence of macro PRJ_HEADERS_PUBLIC
+# that the parent makefile will not define until after including rules-ext.vc
+!if "$(PRJ_HEADERS_PUBLIC)" != ""
+default-install: default-install-headers
+default-install-headers:
+ @echo Installing headers to '$(INCLUDE_INSTALL_DIR)'
+ @for %f in ($(PRJ_HEADERS_PUBLIC)) do @$(COPY) %f "$(INCLUDE_INSTALL_DIR)"
+!endif
+
+!if "$(DISABLE_STANDARD_TARGETS)" == ""
+DISABLE_STANDARD_TARGETS = 0
+!endif
+
+!if "$(DISABLE_TARGET_setup)" == ""
+DISABLE_TARGET_setup = 0
+!endif
+!if "$(DISABLE_TARGET_install)" == ""
+DISABLE_TARGET_install = 0
+!endif
+!if "$(DISABLE_TARGET_clean)" == ""
+DISABLE_TARGET_clean = 0
+!endif
+!if "$(DISABLE_TARGET_test)" == ""
+DISABLE_TARGET_test = 0
+!endif
+!if "$(DISABLE_TARGET_shell)" == ""
+DISABLE_TARGET_shell = 0
+!endif
+
+!if !$(DISABLE_STANDARD_TARGETS)
+!if !$(DISABLE_TARGET_setup)
+setup: default-setup
+!endif
+!if !$(DISABLE_TARGET_install)
+install: default-install
+!endif
+!if !$(DISABLE_TARGET_clean)
+clean: default-clean
+realclean: hose
+hose: default-hose
+distclean: realclean default-distclean
+!endif
+!if !$(DISABLE_TARGET_test)
+test: default-test
+!endif
+!if !$(DISABLE_TARGET_shell)
+shell: default-shell
+!endif
+!endif # DISABLE_STANDARD_TARGETS
diff --git a/sqlite/config.guess b/sqlite/config.guess
index 34093cc6..ae713942 100644
--- a/sqlite/config.guess
+++ b/sqlite/config.guess
@@ -1,14 +1,12 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-# Inc.
+# Copyright 1992-2019 Free Software Foundation, Inc.
-timestamp='2007-07-22'
+timestamp='2019-05-28'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
@@ -17,26 +15,22 @@ timestamp='2007-07-22'
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program; if not, see .
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-
-# Originally written by Per Bothner .
-# Please send patches to . Submit a context
-# diff and a properly formatted ChangeLog entry.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
#
-# This script attempts to guess a canonical system name similar to
-# config.sub. If it succeeds, it prints the system name on stdout, and
-# exits with 0. Otherwise, it exits with 1.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to .
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -45,7 +39,7 @@ Usage: $0 [OPTION]
Output the configuration name of the system \`$me' is run on.
-Operation modes:
+Options:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
@@ -56,8 +50,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-Free Software Foundation, Inc.
+Copyright 1992-2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -91,8 +84,6 @@ if test $# != 0; then
exit 1
fi
-trap 'exit 1' 1 2 15
-
# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
@@ -103,34 +94,38 @@ trap 'exit 1' 1 2 15
# Portable tmp directory creation inspired by the Autoconf team.
-set_cc_for_build='
-trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
-trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
-: ${TMPDIR=/tmp} ;
- { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
- { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
- { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
- { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
-dummy=$tmp/dummy ;
-tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
-case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,) echo "int x;" > $dummy.c ;
- for c in cc gcc c89 c99 ; do
- if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
- CC_FOR_BUILD="$c"; break ;
- fi ;
- done ;
- if test x"$CC_FOR_BUILD" = x ; then
- CC_FOR_BUILD=no_compiler_found ;
- fi
- ;;
- ,,*) CC_FOR_BUILD=$CC ;;
- ,*,*) CC_FOR_BUILD=$HOST_CC ;;
-esac ; set_cc_for_build= ;'
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+ : "${TMPDIR=/tmp}"
+ # shellcheck disable=SC2039
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+ dummy=$tmp/dummy
+ case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+ ,,) echo "int x;" > "$dummy.c"
+ for driver in cc gcc c89 c99 ; do
+ if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$driver"
+ break
+ fi
+ done
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+ esac
+}
# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
-if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+if test -f /.attbin/uname ; then
PATH=$PATH:/.attbin ; export PATH
fi
@@ -139,29 +134,40 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
-if [ "${UNAME_SYSTEM}" = "Linux" ] ; then
- eval $set_cc_for_build
- cat << EOF > $dummy.c
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ LIBC=gnu
+
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
#include
- #ifdef __UCLIBC__
- # ifdef __UCLIBC_CONFIG_VERSION__
- LIBC=uclibc __UCLIBC_CONFIG_VERSION__
- # else
+ #if defined(__UCLIBC__)
LIBC=uclibc
- # endif
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
#else
LIBC=gnu
#endif
-EOF
- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep LIBC= | sed -e 's: ::g'`
-fi
+ EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+ # If ldd exists, use it to detect musl libc.
+ if command -v ldd >/dev/null && \
+ ldd --version 2>&1 | grep -q ^musl
+ then
+ LIBC=musl
+ fi
+ ;;
+esac
# Note: order is significant - the case branches are not exclusive.
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
- # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -171,23 +177,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
sysctl="sysctl -n hw.machine_arch"
- UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
- /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
- case "${UNAME_MACHINE_ARCH}" in
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ "/sbin/$sysctl" 2>/dev/null || \
+ "/usr/sbin/$sysctl" 2>/dev/null || \
+ echo unknown)`
+ case "$UNAME_MACHINE_ARCH" in
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
- *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ earmv*)
+ arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine="${arch}${endian}"-unknown
+ ;;
+ *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
esac
# The Operating System including object format, if it has switched
- # to ELF recently, or will in the future.
- case "${UNAME_MACHINE_ARCH}" in
+ # to ELF recently (or will in the future) and ABI.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ os=netbsdelf
+ ;;
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
- eval $set_cc_for_build
+ set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
- | grep __ELF__ >/dev/null
+ | grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -197,7 +213,14 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
;;
esac
# The OS release
@@ -205,42 +228,62 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# thus, need a distinct triplet. However, they do not need
# kernel version information, so it can be replaced with a
# suitable tag, in the style of linux-gnu.
- case "${UNAME_VERSION}" in
+ case "$UNAME_VERSION" in
Debian*)
release='-gnu'
;;
*)
- release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
- echo "${machine}-${os}${release}"
+ echo "$machine-${os}${release}${abi-}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
- echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+ exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+ exit ;;
+ *:MidnightBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
exit ;;
*:ekkoBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
exit ;;
*:SolidBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
exit ;;
macppc:MirBSD:*:*)
- echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
exit ;;
*:MirBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:Sortix:*:*)
+ echo "$UNAME_MACHINE"-unknown-sortix
+ exit ;;
+ *:Redox:*:*)
+ echo "$UNAME_MACHINE"-unknown-redox
exit ;;
+ mips:OSF1:*.*)
+ echo mips-dec-osf1
+ exit ;;
alpha:OSF1:*:*)
case $UNAME_RELEASE in
*4.0)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -250,60 +293,54 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
case "$ALPHA_CPU_TYPE" in
"EV4 (21064)")
- UNAME_MACHINE="alpha" ;;
+ UNAME_MACHINE=alpha ;;
"EV4.5 (21064)")
- UNAME_MACHINE="alpha" ;;
+ UNAME_MACHINE=alpha ;;
"LCA4 (21066/21068)")
- UNAME_MACHINE="alpha" ;;
+ UNAME_MACHINE=alpha ;;
"EV5 (21164)")
- UNAME_MACHINE="alphaev5" ;;
+ UNAME_MACHINE=alphaev5 ;;
"EV5.6 (21164A)")
- UNAME_MACHINE="alphaev56" ;;
+ UNAME_MACHINE=alphaev56 ;;
"EV5.6 (21164PC)")
- UNAME_MACHINE="alphapca56" ;;
+ UNAME_MACHINE=alphapca56 ;;
"EV5.7 (21164PC)")
- UNAME_MACHINE="alphapca57" ;;
+ UNAME_MACHINE=alphapca57 ;;
"EV6 (21264)")
- UNAME_MACHINE="alphaev6" ;;
+ UNAME_MACHINE=alphaev6 ;;
"EV6.7 (21264A)")
- UNAME_MACHINE="alphaev67" ;;
+ UNAME_MACHINE=alphaev67 ;;
"EV6.8CB (21264C)")
- UNAME_MACHINE="alphaev68" ;;
+ UNAME_MACHINE=alphaev68 ;;
"EV6.8AL (21264B)")
- UNAME_MACHINE="alphaev68" ;;
+ UNAME_MACHINE=alphaev68 ;;
"EV6.8CX (21264D)")
- UNAME_MACHINE="alphaev68" ;;
+ UNAME_MACHINE=alphaev68 ;;
"EV6.9A (21264/EV69A)")
- UNAME_MACHINE="alphaev69" ;;
+ UNAME_MACHINE=alphaev69 ;;
"EV7 (21364)")
- UNAME_MACHINE="alphaev7" ;;
+ UNAME_MACHINE=alphaev7 ;;
"EV7.9 (21364A)")
- UNAME_MACHINE="alphaev79" ;;
+ UNAME_MACHINE=alphaev79 ;;
esac
# A Pn.n version is a patched version.
# A Vn.n version is a released version.
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
- echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
- exit ;;
- Alpha\ *:Windows_NT*:*)
- # How do we know it's Interix rather than the generic POSIX subsystem?
- # Should we change UNAME_MACHINE based on the output of uname instead
- # of the specific Alpha model?
- echo alpha-pc-interix
- exit ;;
- 21064:Windows_NT:50:3)
- echo alpha-dec-winnt3.5
- exit ;;
+ echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
Amiga*:UNIX_System_V:4.0:*)
echo m68k-unknown-sysv4
exit ;;
*:[Aa]miga[Oo][Ss]:*:*)
- echo ${UNAME_MACHINE}-unknown-amigaos
+ echo "$UNAME_MACHINE"-unknown-amigaos
exit ;;
*:[Mm]orph[Oo][Ss]:*:*)
- echo ${UNAME_MACHINE}-unknown-morphos
+ echo "$UNAME_MACHINE"-unknown-morphos
exit ;;
*:OS/390:*:*)
echo i370-ibm-openedition
@@ -312,12 +349,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-os400
+ echo powerpc-ibm-os400
exit ;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
- echo arm-acorn-riscix${UNAME_RELEASE}
+ echo arm-acorn-riscix"$UNAME_RELEASE"
exit ;;
- arm:riscos:*:*|arm:RISCOS:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -341,20 +378,39 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
+ s390x:SunOS:*:*)
+ echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
sun4H:SunOS:5.*:*)
- echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
exit ;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
- echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux"$UNAME_RELEASE"
exit ;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
- echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ set_cc_for_build
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but
# it's likely to be more like Solaris than SunOS4.
- echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
exit ;;
sun4*:SunOS:*:*)
case "`/usr/bin/arch -k`" in
@@ -363,25 +419,25 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
;;
esac
# Japanese Language versions have a version number like `4.1.3-JL'.
- echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
exit ;;
sun3*:SunOS:*:*)
- echo m68k-sun-sunos${UNAME_RELEASE}
+ echo m68k-sun-sunos"$UNAME_RELEASE"
exit ;;
sun*:*:4.2BSD:*)
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
- test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
case "`/bin/arch`" in
sun3)
- echo m68k-sun-sunos${UNAME_RELEASE}
+ echo m68k-sun-sunos"$UNAME_RELEASE"
;;
sun4)
- echo sparc-sun-sunos${UNAME_RELEASE}
+ echo sparc-sun-sunos"$UNAME_RELEASE"
;;
esac
exit ;;
aushp:SunOS:*:*)
- echo sparc-auspex-sunos${UNAME_RELEASE}
+ echo sparc-auspex-sunos"$UNAME_RELEASE"
exit ;;
# The situation for MiNT is a little confusing. The machine name
# can be virtually everything (everything which is not
@@ -392,44 +448,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# MiNT. But MiNT is downward compatible to TOS, so this should
# be no problem.
atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint"$UNAME_RELEASE"
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint"$UNAME_RELEASE"
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint"$UNAME_RELEASE"
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint"$UNAME_RELEASE"
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint"$UNAME_RELEASE"
+ exit ;;
m68k:machten:*:*)
- echo m68k-apple-machten${UNAME_RELEASE}
+ echo m68k-apple-machten"$UNAME_RELEASE"
exit ;;
powerpc:machten:*:*)
- echo powerpc-apple-machten${UNAME_RELEASE}
+ echo powerpc-apple-machten"$UNAME_RELEASE"
exit ;;
RISC*:Mach:*:*)
echo mips-dec-mach_bsd4.3
exit ;;
RISC*:ULTRIX:*:*)
- echo mips-dec-ultrix${UNAME_RELEASE}
+ echo mips-dec-ultrix"$UNAME_RELEASE"
exit ;;
VAX*:ULTRIX*:*:*)
- echo vax-dec-ultrix${UNAME_RELEASE}
+ echo vax-dec-ultrix"$UNAME_RELEASE"
exit ;;
2020:CLIX:*:* | 2430:CLIX:*:*)
- echo clipper-intergraph-clix${UNAME_RELEASE}
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
exit ;;
mips:*:*:UMIPS | mips:*:*:RISCos)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#ifdef __cplusplus
#include /* for printf() prototype */
int main (int argc, char *argv[]) {
@@ -438,23 +494,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
#endif
#if defined (host_mips) && defined (MIPSEB)
#if defined (SYSTYPE_SYSV)
- printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
#endif
#if defined (SYSTYPE_SVR4)
- printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
#endif
#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
- printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
#endif
#endif
exit (-1);
}
EOF
- $CC_FOR_BUILD -o $dummy $dummy.c &&
- dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
- SYSTEM_NAME=`$dummy $dummyarg` &&
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
{ echo "$SYSTEM_NAME"; exit; }
- echo mips-mips-riscos${UNAME_RELEASE}
+ echo mips-mips-riscos"$UNAME_RELEASE"
exit ;;
Motorola:PowerMAX_OS:*:*)
echo powerpc-motorola-powermax
@@ -478,21 +534,21 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
- if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
then
- if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
- [ ${TARGET_BINARY_INTERFACE}x = x ]
+ if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+ [ "$TARGET_BINARY_INTERFACE"x = x ]
then
- echo m88k-dg-dgux${UNAME_RELEASE}
+ echo m88k-dg-dgux"$UNAME_RELEASE"
else
- echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
fi
else
- echo i586-dg-dgux${UNAME_RELEASE}
+ echo i586-dg-dgux"$UNAME_RELEASE"
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -507,7 +563,7 @@ EOF
echo m68k-tektronix-bsd
exit ;;
*:IRIX*:*:*)
- echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
exit ;;
????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
@@ -519,14 +575,14 @@ EOF
if [ -x /usr/bin/oslevel ] ; then
IBM_REV=`/usr/bin/oslevel`
else
- IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi
- echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
exit ;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#include
main()
@@ -537,7 +593,7 @@ EOF
exit(0);
}
EOF
- if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
then
echo "$SYSTEM_NAME"
else
@@ -549,28 +605,29 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[45])
+ *:AIX:*:[4567])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
- if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
IBM_ARCH=powerpc
fi
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
else
- IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi
- echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
exit ;;
*:AIX:*:*)
echo rs6000-ibm-aix
exit ;;
- ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
echo romp-ibm-bsd4.4
exit ;;
ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
- echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
exit ;; # report: romp-ibm BSD 4.3
*:BOSX:*:*)
echo rs6000-bull-bosx
@@ -585,67 +642,67 @@ EOF
echo m68k-hp-bsd4.4
exit ;;
9000/[34678]??:HP-UX:*:*)
- HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
- case "${UNAME_MACHINE}" in
- 9000/31? ) HP_ARCH=m68000 ;;
- 9000/[34]?? ) HP_ARCH=m68k ;;
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ case "$UNAME_MACHINE" in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
9000/[678][0-9][0-9])
if [ -x /usr/bin/getconf ]; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
- sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
- case "${sc_cpu_version}" in
- 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
- 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
- 532) # CPU_PA_RISC2_0
- case "${sc_kernel_bits}" in
- 32) HP_ARCH="hppa2.0n" ;;
- 64) HP_ARCH="hppa2.0w" ;;
- '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
- esac ;;
- esac
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "$sc_cpu_version" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "$sc_kernel_bits" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
fi
- if [ "${HP_ARCH}" = "" ]; then
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ if [ "$HP_ARCH" = "" ]; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
- #define _HPUX_SOURCE
- #include
- #include
+ #define _HPUX_SOURCE
+ #include
+ #include
- int main ()
- {
- #if defined(_SC_KERNEL_BITS)
- long bits = sysconf(_SC_KERNEL_BITS);
- #endif
- long cpu = sysconf (_SC_CPU_VERSION);
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
- switch (cpu)
- {
- case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
- case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
- case CPU_PA_RISC2_0:
- #if defined(_SC_KERNEL_BITS)
- switch (bits)
- {
- case 64: puts ("hppa2.0w"); break;
- case 32: puts ("hppa2.0n"); break;
- default: puts ("hppa2.0"); break;
- } break;
- #else /* !defined(_SC_KERNEL_BITS) */
- puts ("hppa2.0"); break;
- #endif
- default: puts ("hppa1.0"); break;
- }
- exit (0);
- }
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
EOF
- (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
- if [ ${HP_ARCH} = "hppa2.0w" ]
+ if [ "$HP_ARCH" = hppa2.0w ]
then
- eval $set_cc_for_build
+ set_cc_for_build
# hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
# 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
@@ -656,23 +713,23 @@ EOF
# $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
# => hppa64-hp-hpux11.23
- if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
- grep __LP64__ >/dev/null
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
then
- HP_ARCH="hppa2.0w"
+ HP_ARCH=hppa2.0w
else
- HP_ARCH="hppa64"
+ HP_ARCH=hppa64
fi
fi
- echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
exit ;;
ia64:HP-UX:*:*)
- HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
- echo ia64-hp-hpux${HPUX_REV}
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux"$HPUX_REV"
exit ;;
3050*:HI-UX:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#include
int
main ()
@@ -697,11 +754,11 @@ EOF
exit (0);
}
EOF
- $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
{ echo "$SYSTEM_NAME"; exit; }
echo unknown-hitachi-hiuxwe2
exit ;;
- 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
echo hppa1.1-hp-bsd
exit ;;
9000/8??:4.3bsd:*:*)
@@ -710,7 +767,7 @@ EOF
*9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
echo hppa1.0-hp-mpeix
exit ;;
- hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
echo hppa1.1-hp-osf
exit ;;
hp8??:OSF1:*:*)
@@ -718,9 +775,9 @@ EOF
exit ;;
i*86:OSF1:*:*)
if [ -x /usr/sbin/sysversion ] ; then
- echo ${UNAME_MACHINE}-unknown-osf1mk
+ echo "$UNAME_MACHINE"-unknown-osf1mk
else
- echo ${UNAME_MACHINE}-unknown-osf1
+ echo "$UNAME_MACHINE"-unknown-osf1
fi
exit ;;
parisc*:Lites*:*:*)
@@ -728,317 +785,313 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
- echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*[A-Z]90:*:*:*)
- echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
-e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
-e 's/\.[^.]*$/.X/'
exit ;;
CRAY*TS:*:*:*)
- echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*T3E:*:*:*)
- echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*SV1:*:*:*)
- echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
*:UNICOS/mp:*:*)
- echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
- FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
- FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
- echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
- exit ;;
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
5000:UNIX_System_V:4.*:*)
- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
- FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
- echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
- echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
exit ;;
sparc*:BSD/OS:*:*)
- echo sparc-unknown-bsdi${UNAME_RELEASE}
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
exit ;;
*:BSD/OS:*:*)
- echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ arm:FreeBSD:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ set_cc_for_build
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi
+ else
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf
+ fi
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case "$UNAME_PROCESSOR" in
amd64)
- echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
- *)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
esac
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
exit ;;
i*:CYGWIN*:*)
- echo ${UNAME_MACHINE}-pc-cygwin
+ echo "$UNAME_MACHINE"-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo "$UNAME_MACHINE"-pc-mingw64
exit ;;
*:MINGW*:*)
- echo ${UNAME_MACHINE}-pc-mingw32
+ echo "$UNAME_MACHINE"-pc-mingw32
exit ;;
- i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ *:MSYS*:*)
+ echo "$UNAME_MACHINE"-pc-msys
exit ;;
i*:PW*:*)
- echo ${UNAME_MACHINE}-pc-pw32
+ echo "$UNAME_MACHINE"-pc-pw32
exit ;;
- *:Interix*:[3456]*)
- case ${UNAME_MACHINE} in
+ *:Interix*:*)
+ case "$UNAME_MACHINE" in
x86)
- echo i586-pc-interix${UNAME_RELEASE}
+ echo i586-pc-interix"$UNAME_RELEASE"
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
exit ;;
- EM64T | authenticamd)
- echo x86_64-unknown-interix${UNAME_RELEASE}
+ IA64)
+ echo ia64-unknown-interix"$UNAME_RELEASE"
exit ;;
esac ;;
- [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
- echo i${UNAME_MACHINE}-pc-mks
- exit ;;
- i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
- # How do we know it's Interix rather than the generic POSIX subsystem?
- # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
- # UNAME_MACHINE based on the output of uname instead of i386?
- echo i586-pc-interix
- exit ;;
i*:UWIN*:*)
- echo ${UNAME_MACHINE}-pc-uwin
+ echo "$UNAME_MACHINE"-pc-uwin
exit ;;
amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
- echo x86_64-unknown-cygwin
- exit ;;
- p*:CYGWIN*:*)
- echo powerpcle-unknown-cygwin
+ echo x86_64-pc-cygwin
exit ;;
prep*:SunOS:5.*:*)
- echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
exit ;;
*:GNU:*:*)
# the GNU system
- echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
exit ;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
- echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+ exit ;;
+ *:Minix:*:*)
+ echo "$UNAME_MACHINE"-unknown-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
- i*86:Minix:*:*)
- echo ${UNAME_MACHINE}-pc-minix
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
arm*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+ else
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+ fi
+ fi
exit ;;
avr32*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-${LIBC}
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-${LIBC}
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ e2k:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ hexagon:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
exit ;;
ia64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ k1om:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
m32r*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
m68*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
- mips:Linux:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ mips:Linux:*:* | mips64:Linux:*:*)
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
#undef CPU
#undef mips
#undef mipsel
- #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mipsel
+ #undef mips64
+ #undef mips64el
+ #if ${IS_GLIBC} && defined(_ABI64)
+ LIBCABI=gnuabi64
#else
- #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips
+ #if ${IS_GLIBC} && defined(_ABIN32)
+ LIBCABI=gnuabin32
#else
- CPU=
+ LIBCABI=${LIBC}
#endif
#endif
-EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
- ;;
- mips64:Linux:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #undef CPU
- #undef mips64
- #undef mips64el
+
+ #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa64r6
+ #else
+ #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa32r6
+ #else
+ #if defined(__mips64)
+ CPU=mips64
+ #else
+ CPU=mips
+ #endif
+ #endif
+ #endif
+
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
- CPU=mips64el
+ MIPS_ENDIAN=el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=mips64
+ MIPS_ENDIAN=
#else
- CPU=
+ MIPS_ENDIAN=
#endif
#endif
EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^CPU/{
- s: ::g
- p
- }'`"
- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`"
+ test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-${LIBC}
+ mips64el:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
- ppc:Linux:*:*)
- echo powerpc-unknown-linux-${LIBC}
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-"$LIBC"
exit ;;
- ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-${LIBC}
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
- alpha:Linux:*:*)
- case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
- EV5) UNAME_MACHINE=alphaev5 ;;
- EV56) UNAME_MACHINE=alphaev56 ;;
- PCA56) UNAME_MACHINE=alphapca56 ;;
- PCA57) UNAME_MACHINE=alphapca56 ;;
- EV6) UNAME_MACHINE=alphaev6 ;;
- EV67) UNAME_MACHINE=alphaev67 ;;
- EV68*) UNAME_MACHINE=alphaev68 ;;
- esac
- objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
- if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-"$LIBC"
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-"$LIBC"
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
- PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
- PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
- *) echo hppa-unknown-linux-${LIBC} ;;
+ PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+ PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+ *) echo hppa-unknown-linux-"$LIBC" ;;
esac
exit ;;
- parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-${LIBC}
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-"$LIBC"
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-"$LIBC"
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-"$LIBC"
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-"$LIBC"
+ exit ;;
+ riscv32:Linux:*:* | riscv64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
- echo ${UNAME_MACHINE}-ibm-linux
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
sh*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
sparc:Linux:*:* | sparc64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ tile*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
vax:Linux:*:*)
- echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
exit ;;
- xtensa:Linux:*:*)
- echo xtensa-unknown-linux-${LIBC}
+ xtensa*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
- i*86:Linux:*:*)
- # The BFD linker knows what the default object file format is, so
- # first see if it will tell us. cd to the root directory to prevent
- # problems with other programs or directories called `ld' in the path.
- # Set LC_ALL=C to ensure ld outputs messages in English.
- ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
- | sed -ne '/supported targets:/!d
- s/[ ][ ]*/ /g
- s/.*supported targets: *//
- s/ .*//
- p'`
- case "$ld_supported_targets" in
- elf32-i386)
- TENTATIVE="${UNAME_MACHINE}-pc-linux-${LIBC}"
- ;;
- a.out-i386-linux)
- echo "${UNAME_MACHINE}-pc-linux-${LIBC}aout"
- exit ;;
- coff-i386)
- echo "${UNAME_MACHINE}-pc-linux-${LIBC}coff"
- exit ;;
- "")
- # Either a pre-BFD a.out linker (linux-gnuoldld) or
- # one that does not give us useful --help.
- echo "${UNAME_MACHINE}-pc-linux-${LIBC}oldld"
- exit ;;
- esac
- # This should get integrated into the C code below, but now we hack
- if [ "$LIBC" != "gnu" ] ; then echo "$TENTATIVE" && exit 0 ; fi
- # Determine whether the default compiler is a.out or elf
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #include
- #ifdef __ELF__
- # ifdef __GLIBC__
- # if __GLIBC__ >= 2
- LIBC=gnu
- # else
- LIBC=gnulibc1
- # endif
- # else
- LIBC=gnulibc1
- # endif
- #else
- #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
- LIBC=gnu
- #else
- LIBC=gnuaout
- #endif
- #endif
- #ifdef __dietlibc__
- LIBC=dietlibc
- #endif
-EOF
- eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
- /^LIBC/{
- s: ::g
- p
- }'`"
- test x"${LIBC}" != x && {
- echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
- exit
- }
- test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
- ;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
# earlier versions are messed up and put the nodename in both
@@ -1046,54 +1099,54 @@ EOF
echo i386-sequent-sysv4
exit ;;
i*86:UNIX_SV:4.2MP:2.*)
- # Unixware is an offshoot of SVR4, but it has its own version
- # number series starting with 2...
- # I am not positive that other SVR4 systems won't match this,
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
- # Use sysv4.2uw... so that sysv4* matches it.
- echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
exit ;;
i*86:OS/2:*:*)
# If we were able to find `uname', then EMX Unix compatibility
# is probably installed.
- echo ${UNAME_MACHINE}-pc-os2-emx
+ echo "$UNAME_MACHINE"-pc-os2-emx
exit ;;
i*86:XTS-300:*:STOP)
- echo ${UNAME_MACHINE}-unknown-stop
+ echo "$UNAME_MACHINE"-unknown-stop
exit ;;
i*86:atheos:*:*)
- echo ${UNAME_MACHINE}-unknown-atheos
+ echo "$UNAME_MACHINE"-unknown-atheos
exit ;;
i*86:syllable:*:*)
- echo ${UNAME_MACHINE}-pc-syllable
+ echo "$UNAME_MACHINE"-pc-syllable
exit ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
- echo i386-unknown-lynxos${UNAME_RELEASE}
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
exit ;;
i*86:*DOS:*:*)
- echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
exit ;;
- i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
- UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ i*86:*:4.*:*)
+ UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
- echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
else
- echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
*Pent*|*Celeron) UNAME_MACHINE=i686 ;;
esac
- echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}"
exit ;;
i*86:*:3.2:*)
if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then
UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
@@ -1103,17 +1156,20 @@ EOF
&& UNAME_MACHINE=i686
(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
&& UNAME_MACHINE=i686
- echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
else
- echo ${UNAME_MACHINE}-pc-sysv32
+ echo "$UNAME_MACHINE"-pc-sysv32
fi
exit ;;
pc:*:*:*)
# Left here for compatibility:
- # uname -m prints for DJGPP always 'pc', but it prints nothing about
- # the processor, so we play safe by assuming i386.
- echo i386-pc-msdosdjgpp
- exit ;;
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1122,9 +1178,9 @@ EOF
exit ;;
i860:*:4.*:*) # i860-SVR4
if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
- echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
else # Add other i860-SVR4 vendors below as they are discovered.
- echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
fi
exit ;;
mini*:CTIX:SYS*5:*)
@@ -1144,29 +1200,39 @@ EOF
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
- && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
- && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
- /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
- && { echo i486-ncr-sysv4; exit; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
- echo m68k-unknown-lynxos${UNAME_RELEASE}
+ echo m68k-unknown-lynxos"$UNAME_RELEASE"
exit ;;
mc68030:UNIX_System_V:4.*:*)
echo m68k-atari-sysv4
exit ;;
TSUNAMI:LynxOS:2.*:*)
- echo sparc-unknown-lynxos${UNAME_RELEASE}
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
exit ;;
rs6000:LynxOS:2.*:*)
- echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
exit ;;
- PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
- echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
exit ;;
SM[BE]S:UNIX_SV:*:*)
- echo mips-dde-sysv${UNAME_RELEASE}
+ echo mips-dde-sysv"$UNAME_RELEASE"
exit ;;
RM*:ReliantUNIX-*:*:*)
echo mips-sni-sysv4
@@ -1177,15 +1243,15 @@ EOF
*:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then
UNAME_MACHINE=`(uname -p) 2>/dev/null`
- echo ${UNAME_MACHINE}-sni-sysv4
+ echo "$UNAME_MACHINE"-sni-sysv4
else
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- # says
- echo i586-unisys-sysv4
- exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says
+ echo i586-unisys-sysv4
+ exit ;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes .
# How about differentiating between stratus architectures? -djm
@@ -1197,25 +1263,25 @@ EOF
exit ;;
i*86:VOS:*:*)
# From Paul.Green@stratus.com.
- echo ${UNAME_MACHINE}-stratus-vos
+ echo "$UNAME_MACHINE"-stratus-vos
exit ;;
*:VOS:*:*)
# From Paul.Green@stratus.com.
echo hppa1.1-stratus-vos
exit ;;
mc68*:A/UX:*:*)
- echo m68k-apple-aux${UNAME_RELEASE}
+ echo m68k-apple-aux"$UNAME_RELEASE"
exit ;;
news*:NEWS-OS:6*:*)
echo mips-sony-newsos6
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv"$UNAME_RELEASE"
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv"$UNAME_RELEASE"
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1225,53 +1291,101 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
- echo sx4-nec-superux${UNAME_RELEASE}
+ echo sx4-nec-superux"$UNAME_RELEASE"
exit ;;
SX-5:SUPER-UX:*:*)
- echo sx5-nec-superux${UNAME_RELEASE}
+ echo sx5-nec-superux"$UNAME_RELEASE"
exit ;;
SX-6:SUPER-UX:*:*)
- echo sx6-nec-superux${UNAME_RELEASE}
+ echo sx6-nec-superux"$UNAME_RELEASE"
exit ;;
SX-7:SUPER-UX:*:*)
- echo sx7-nec-superux${UNAME_RELEASE}
+ echo sx7-nec-superux"$UNAME_RELEASE"
exit ;;
SX-8:SUPER-UX:*:*)
- echo sx8-nec-superux${UNAME_RELEASE}
+ echo sx8-nec-superux"$UNAME_RELEASE"
exit ;;
SX-8R:SUPER-UX:*:*)
- echo sx8r-nec-superux${UNAME_RELEASE}
+ echo sx8r-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux"$UNAME_RELEASE"
exit ;;
Power*:Rhapsody:*:*)
- echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
exit ;;
*:Rhapsody:*:*)
- echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
exit ;;
*:Darwin:*:*)
- UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in
unknown) UNAME_PROCESSOR=powerpc ;;
esac
- echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ if command -v xcode-select > /dev/null 2> /dev/null && \
+ ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+ # Avoid executing cc if there is no toolchain installed as
+ # cc will be a stub that puts up a graphical alert
+ # prompting the user to install developer tools.
+ CC_FOR_BUILD=no_compiler_found
+ else
+ set_cc_for_build
+ fi
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
+ fi
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
exit ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
UNAME_PROCESSOR=`uname -p`
- if test "$UNAME_PROCESSOR" = "x86"; then
+ if test "$UNAME_PROCESSOR" = x86; then
UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
fi
- echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
exit ;;
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
- echo nse-tandem-nsk${UNAME_RELEASE}
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
exit ;;
- NSR-?:NONSTOP_KERNEL:*:*)
- echo nsr-tandem-nsk${UNAME_RELEASE}
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ echo nsv-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
exit ;;
*:NonStop-UX:*:*)
echo mips-compaq-nonstopux
@@ -1280,18 +1394,19 @@ EOF
echo bs2000-siemens-sysv
exit ;;
DS/*:UNIX_System_V:*:*)
- echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
exit ;;
*:Plan9:*:*)
# "uname -m" is not consistent, so use $cputype instead. 386
# is converted to i386 for consistency with other x86
# operating systems.
- if test "$cputype" = "386"; then
+ # shellcheck disable=SC2154
+ if test "$cputype" = 386; then
UNAME_MACHINE=i386
else
UNAME_MACHINE="$cputype"
fi
- echo ${UNAME_MACHINE}-unknown-plan9
+ echo "$UNAME_MACHINE"-unknown-plan9
exit ;;
*:TOPS-10:*:*)
echo pdp10-unknown-tops10
@@ -1312,14 +1427,14 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ echo mips-sei-seiux"$UNAME_RELEASE"
exit ;;
*:DragonFly:*:*)
- echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
exit ;;
*:*VMS:*:*)
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
- case "${UNAME_MACHINE}" in
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "$UNAME_MACHINE" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
V*) echo vax-dec-vms ; exit ;;
@@ -1328,21 +1443,39 @@ EOF
echo i386-pc-xenix
exit ;;
i*86:skyos:*:*)
- echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
exit ;;
i*86:rdos:*:*)
- echo ${UNAME_MACHINE}-pc-rdos
+ echo "$UNAME_MACHINE"-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo "$UNAME_MACHINE"-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo "$UNAME_MACHINE"-unknown-esx
+ exit ;;
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
+ exit ;;
+ *:Unleashed:*:*)
+ echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
exit ;;
esac
-#echo '(No uname command or uname output not recognized.)' 1>&2
-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
-
-eval $set_cc_for_build
-cat >$dummy.c < "$dummy.c" <
-# include
+#include
+#include
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include
+#endif
+#endif
#endif
main ()
{
@@ -1355,20 +1488,12 @@ main ()
#include
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
- "4"
+ "4"
#else
- ""
+ ""
#endif
- ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
- printf ("arm-acorn-riscix\n"); exit (0);
+ ); exit (0);
#endif
-
-#if defined (hp300) && !defined (hpux)
- printf ("m68k-hp-bsd\n"); exit (0);
#endif
#if defined (NeXT)
@@ -1410,39 +1535,54 @@ main ()
#endif
#if defined (_SEQUENT_)
- struct utsname un;
-
- uname(&un);
-
- if (strncmp(un.version, "V2", 2) == 0) {
- printf ("i386-sequent-ptx2\n"); exit (0);
- }
- if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
- printf ("i386-sequent-ptx1\n"); exit (0);
- }
- printf ("i386-sequent-ptx\n"); exit (0);
+ struct utsname un;
+ uname(&un);
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
#endif
#if defined (vax)
-# if !defined (ultrix)
-# include
-# if defined (BSD)
-# if BSD == 43
- printf ("vax-dec-bsd4.3\n"); exit (0);
-# else
-# if BSD == 199006
- printf ("vax-dec-bsd4.3reno\n"); exit (0);
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# endif
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# else
- printf ("vax-dec-ultrix\n"); exit (0);
-# endif
+#if !defined (ultrix)
+#include
+#if defined (BSD)
+#if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname un;
+ uname (&un);
+ printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname *un;
+ uname (&un);
+ printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
#endif
#if defined (alliant) && defined (i860)
@@ -1453,54 +1593,38 @@ main ()
}
EOF
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` &&
{ echo "$SYSTEM_NAME"; exit; }
# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+echo "$0: unable to guess system type" >&2
-# Convex versions that predate uname can use getsysinfo(1)
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <&2 < in order to provide the needed
-information to handle your system.
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
config.guess timestamp = $timestamp
@@ -1519,16 +1643,16 @@ hostinfo = `(hostinfo) 2>/dev/null`
/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
-UNAME_MACHINE = ${UNAME_MACHINE}
-UNAME_RELEASE = ${UNAME_RELEASE}
-UNAME_SYSTEM = ${UNAME_SYSTEM}
-UNAME_VERSION = ${UNAME_VERSION}
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
EOF
exit 1
# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
diff --git a/sqlite/config.sub b/sqlite/config.sub
index 63cdd0a3..5b158ac4 100644
--- a/sqlite/config.sub
+++ b/sqlite/config.sub
@@ -1,44 +1,40 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-# Inc.
+# Copyright 1992-2019 Free Software Foundation, Inc.
-timestamp='2007-06-28'
+timestamp='2019-05-23'
-# This file is (in principle) common to ALL GNU software.
-# The presence of a machine in this file suggests that SOME GNU software
-# can handle that machine. It does not imply ALL GNU software can.
-#
-# This file is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program; if not, see .
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to . Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Please send patches to .
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
# If it is invalid, we print an error message on stderr and exit with code 1.
# Otherwise, we print the canonical config type on stdout and succeed.
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
# that are meaningful with *any* GNU software.
@@ -57,12 +53,11 @@ timestamp='2007-06-28'
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
- $0 [OPTION] ALIAS
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
Canonicalize a configuration name.
-Operation modes:
+Options:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
@@ -72,8 +67,7 @@ Report bugs and patches to ."
version="\
GNU config.sub ($timestamp)
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-Free Software Foundation, Inc.
+Copyright 1992-2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -95,12 +89,12 @@ while test $# -gt 0 ; do
- ) # Use stdin as input.
break ;;
-* )
- echo "$me: invalid option $1$help"
+ echo "$me: invalid option $1$help" >&2
exit 1 ;;
*local*)
# First pass through any local machine types.
- echo $1
+ echo "$1"
exit ;;
* )
@@ -116,1079 +110,1167 @@ case $# in
exit 1;;
esac
-# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
-# Here we must recognize all the valid KERNEL-OS combinations.
-maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
-case $maybe_os in
- nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
- uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
- storm-chaos* | os2-emx* | rtmk-nova*)
- os=-$maybe_os
- basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
- ;;
- *)
- basic_machine=`echo $1 | sed 's/-[^-]*$//'`
- if [ $basic_machine != $1 ]
- then os=`echo $1 | sed 's/.*-/-/'`
- else os=; fi
- ;;
-esac
+# Split fields of configuration type
+# shellcheck disable=SC2162
+IFS="-" read field1 field2 field3 field4 <&2
+ exit 1
;;
- -sco*)
- os=-sco3.2v2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ os=$field3-$field4
;;
- -udk*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ *-*-*)
+ # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+ # parts
+ maybe_os=$field2-$field3
+ case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
+ | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+ | storm-chaos* | os2-emx* | rtmk-nova*)
+ basic_machine=$field1
+ os=$maybe_os
+ ;;
+ android-linux)
+ basic_machine=$field1-unknown
+ os=linux-android
+ ;;
+ *)
+ basic_machine=$field1-$field2
+ os=$field3
+ ;;
+ esac
;;
- -isc)
- os=-isc2.2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ *-*)
+ # A lone config we happen to match not fitting any pattern
+ case $field1-$field2 in
+ decstation-3100)
+ basic_machine=mips-dec
+ os=
+ ;;
+ *-*)
+ # Second component is usually, but not always the OS
+ case $field2 in
+ # Prevent following clause from handling this valid os
+ sun*os*)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ # Manufacturers
+ dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+ | unicom* | ibm* | next | hp | isi* | apollo | altos* \
+ | convergent* | ncr* | news | 32* | 3600* | 3100* \
+ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+ | ultra | tti* | harris | dolphin | highlevel | gould \
+ | cbm | ns | masscomp | apple | axis | knuth | cray \
+ | microblaze* | sim | cisco \
+ | oki | wec | wrs | winbond)
+ basic_machine=$field1-$field2
+ os=
+ ;;
+ *)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ esac
+ ;;
+ esac
;;
- -clix*)
- basic_machine=clipper-intergraph
+ *)
+ # Convert single-component short-hands not valid as part of
+ # multi-component configurations.
+ case $field1 in
+ 386bsd)
+ basic_machine=i386-pc
+ os=bsd
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=scout
+ ;;
+ alliant)
+ basic_machine=fx80-alliant
+ os=
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ os=
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=sysv
+ ;;
+ amiga)
+ basic_machine=m68k-unknown
+ os=
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=linux
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=bsd
+ ;;
+ cray)
+ basic_machine=j90-cray
+ os=unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ os=
+ ;;
+ da30)
+ basic_machine=m68k-da30
+ os=
+ ;;
+ decstation | pmax | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ os=
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=msdosdjgpp
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=ebmon
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=ose
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=go32
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=sysv3
+ ;;
+ hp300)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=hpux
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=proelf
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=mach
+ ;;
+ vsta)
+ basic_machine=i386-pc
+ os=vsta
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=sysv
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=mingw32ce
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=msdos
+ ;;
+ msys)
+ basic_machine=i686-pc
+ os=msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-pc
+ os=netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=sysv
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=nonstopux
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=os68k
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=linux
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ os=
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=sysv2
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ os=
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ os=
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=sunos4
+ ;;
+ sun3)
+ basic_machine=m68k-sun
+ os=
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=sunos4
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ os=
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=solaris2
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ os=
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=unicos
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=tops20
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=vms
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=vxworks
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=unicos
+ ;;
+ *)
+ basic_machine=$1
+ os=
+ ;;
+ esac
;;
- -isc*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+ # Here we handle the default manufacturer of certain CPU types. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ cpu=hppa1.1
+ vendor=winbond
;;
- -lynx*)
- os=-lynxos
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
;;
- -ptx*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
;;
- -windowsnt*)
- os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ibm*)
+ cpu=i370
+ vendor=ibm
;;
- -psos*)
- os=-psos
+ orion105)
+ cpu=clipper
+ vendor=highlevel
;;
- -mint | -mint[0-9]*)
- basic_machine=m68k-atari
- os=-mint
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
;;
-esac
-
-# Decode aliases for certain CPU-COMPANY combinations.
-case $basic_machine in
- # Recognize the basic CPU types without company name.
- # Some are omitted here because they have special meanings below.
- 1750a | 580 \
- | a29k \
- | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
- | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
- | am33_2.0 \
- | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
- | bfin \
- | c4x | clipper \
- | d10v | d30v | dlx | dsp16xx | dvp \
- | fido | fr30 | frv \
- | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
- | i370 | i860 | i960 | ia64 \
- | ip2k | iq2000 \
- | m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep \
- | mips | mipsbe | mipseb | mipsel | mipsle \
- | mips16 \
- | mips64 | mips64el \
- | mips64vr | mips64vrel \
- | mips64orion | mips64orionel \
- | mips64vr4100 | mips64vr4100el \
- | mips64vr4300 | mips64vr4300el \
- | mips64vr5000 | mips64vr5000el \
- | mips64vr5900 | mips64vr5900el \
- | mipsisa32 | mipsisa32el \
- | mipsisa32r2 | mipsisa32r2el \
- | mipsisa64 | mipsisa64el \
- | mipsisa64r2 | mipsisa64r2el \
- | mipsisa64sb1 | mipsisa64sb1el \
- | mipsisa64sr71k | mipsisa64sr71kel \
- | mipstx39 | mipstx39el \
- | mn10200 | mn10300 \
- | mt \
- | msp430 \
- | nios | nios2 \
- | ns16k | ns32k \
- | or32 \
- | pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
- | pyramid \
- | score \
- | sh | sh[1234] | sh[24]a | sh[24]a*eb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
- | sh64 | sh64le \
- | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
- | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
- | spu | strongarm \
- | tahoe | thumb | tic4x | tic80 | tron \
- | v850 | v850e \
- | we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
- | z8k)
- basic_machine=$basic_machine-unknown
- ;;
- m6811 | m68hc11 | m6812 | m68hc12)
- # Motorola 68HC11/12.
- basic_machine=$basic_machine-unknown
- os=-none
- ;;
- m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
- ;;
- ms1)
- basic_machine=mt-unknown
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
;;
- # We use `pc' rather than `unknown'
- # because (1) that's what they normally are, and
- # (2) the word "unknown" tends to confuse beginning users.
- i*86 | x86_64)
- basic_machine=$basic_machine-pc
- ;;
- # Object if more than one company name word.
- *-*-*)
- echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
- exit 1
- ;;
- # Recognize the basic CPU types with company name.
- 580-* \
- | a29k-* \
- | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
- | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
- | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
- | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
- | avr-* | avr32-* \
- | bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
- | clipper-* | craynv-* | cydra-* \
- | d10v-* | d30v-* | dlx-* \
- | elxsi-* \
- | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
- | h8300-* | h8500-* \
- | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
- | i*86-* | i860-* | i960-* | ia64-* \
- | ip2k-* | iq2000-* \
- | m32c-* | m32r-* | m32rle-* \
- | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* \
- | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
- | mips16-* \
- | mips64-* | mips64el-* \
- | mips64vr-* | mips64vrel-* \
- | mips64orion-* | mips64orionel-* \
- | mips64vr4100-* | mips64vr4100el-* \
- | mips64vr4300-* | mips64vr4300el-* \
- | mips64vr5000-* | mips64vr5000el-* \
- | mips64vr5900-* | mips64vr5900el-* \
- | mipsisa32-* | mipsisa32el-* \
- | mipsisa32r2-* | mipsisa32r2el-* \
- | mipsisa64-* | mipsisa64el-* \
- | mipsisa64r2-* | mipsisa64r2el-* \
- | mipsisa64sb1-* | mipsisa64sb1el-* \
- | mipsisa64sr71k-* | mipsisa64sr71kel-* \
- | mipstx39-* | mipstx39el-* \
- | mmix-* \
- | mt-* \
- | msp430-* \
- | nios-* | nios2-* \
- | none-* | np1-* | ns16k-* | ns32k-* \
- | orion-* \
- | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
- | pyramid-* \
- | romp-* | rs6000-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[24]a*eb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
- | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
- | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
- | sparclite-* \
- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
- | tahoe-* | thumb-* \
- | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
- | tron-* \
- | v850-* | v850e-* | vax-* \
- | we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
- | xstormy16-* | xtensa-* \
- | ymp-* \
- | z8k-*)
- ;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
- 386bsd)
- basic_machine=i386-unknown
- os=-bsd
- ;;
3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
- basic_machine=m68000-att
+ cpu=m68000
+ vendor=att
;;
3b*)
- basic_machine=we32k-att
- ;;
- a29khif)
- basic_machine=a29k-amd
- os=-udi
- ;;
- abacus)
- basic_machine=abacus-unknown
- ;;
- adobe68k)
- basic_machine=m68010-adobe
- os=-scout
- ;;
- alliant | fx80)
- basic_machine=fx80-alliant
- ;;
- altos | altos3068)
- basic_machine=m68k-altos
- ;;
- am29k)
- basic_machine=a29k-none
- os=-bsd
- ;;
- amd64)
- basic_machine=x86_64-pc
- ;;
- amd64-*)
- basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- amdahl)
- basic_machine=580-amdahl
- os=-sysv
- ;;
- amiga | amiga-*)
- basic_machine=m68k-unknown
- ;;
- amigaos | amigados)
- basic_machine=m68k-unknown
- os=-amigaos
- ;;
- amigaunix | amix)
- basic_machine=m68k-unknown
- os=-sysv4
- ;;
- apollo68)
- basic_machine=m68k-apollo
- os=-sysv
- ;;
- apollo68bsd)
- basic_machine=m68k-apollo
- os=-bsd
- ;;
- aux)
- basic_machine=m68k-apple
- os=-aux
- ;;
- balance)
- basic_machine=ns32k-sequent
- os=-dynix
- ;;
- c90)
- basic_machine=c90-cray
- os=-unicos
- ;;
- convex-c1)
- basic_machine=c1-convex
- os=-bsd
- ;;
- convex-c2)
- basic_machine=c2-convex
- os=-bsd
- ;;
- convex-c32)
- basic_machine=c32-convex
- os=-bsd
- ;;
- convex-c34)
- basic_machine=c34-convex
- os=-bsd
- ;;
- convex-c38)
- basic_machine=c38-convex
- os=-bsd
- ;;
- cray | j90)
- basic_machine=j90-cray
- os=-unicos
- ;;
- craynv)
- basic_machine=craynv-cray
- os=-unicosmp
- ;;
- cr16)
- basic_machine=cr16-unknown
- os=-elf
- ;;
- crds | unos)
- basic_machine=m68k-crds
- ;;
- crisv32 | crisv32-* | etraxfs*)
- basic_machine=crisv32-axis
- ;;
- cris | cris-* | etrax*)
- basic_machine=cris-axis
- ;;
- crx)
- basic_machine=crx-unknown
- os=-elf
+ cpu=we32k
+ vendor=att
;;
- da30 | da30-*)
- basic_machine=m68k-da30
- ;;
- decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
- basic_machine=mips-dec
+ bluegene*)
+ cpu=powerpc
+ vendor=ibm
+ os=cnk
;;
decsystem10* | dec10*)
- basic_machine=pdp10-dec
- os=-tops10
+ cpu=pdp10
+ vendor=dec
+ os=tops10
;;
decsystem20* | dec20*)
- basic_machine=pdp10-dec
- os=-tops20
+ cpu=pdp10
+ vendor=dec
+ os=tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
- basic_machine=m68k-motorola
- ;;
- delta88)
- basic_machine=m88k-motorola
- os=-sysv3
- ;;
- djgpp)
- basic_machine=i586-pc
- os=-msdosdjgpp
- ;;
- dpx20 | dpx20-*)
- basic_machine=rs6000-bull
- os=-bosx
- ;;
- dpx2* | dpx2*-bull)
- basic_machine=m68k-bull
- os=-sysv3
- ;;
- ebmon29k)
- basic_machine=a29k-amd
- os=-ebmon
+ cpu=m68k
+ vendor=motorola
;;
- elxsi)
- basic_machine=elxsi-elxsi
- os=-bsd
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ os=sysv3
;;
encore | umax | mmax)
- basic_machine=ns32k-encore
+ cpu=ns32k
+ vendor=encore
;;
- es1800 | OSE68k | ose68k | ose | OSE)
- basic_machine=m68k-ericsson
- os=-ose
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ os=${os:-bsd}
;;
fx2800)
- basic_machine=i860-alliant
+ cpu=i860
+ vendor=alliant
;;
genix)
- basic_machine=ns32k-ns
- ;;
- gmicro)
- basic_machine=tron-gmicro
- os=-sysv
- ;;
- go32)
- basic_machine=i386-pc
- os=-go32
+ cpu=ns32k
+ vendor=ns
;;
h3050r* | hiux*)
- basic_machine=hppa1.1-hitachi
- os=-hiuxwe2
- ;;
- h8300hms)
- basic_machine=h8300-hitachi
- os=-hms
- ;;
- h8300xray)
- basic_machine=h8300-hitachi
- os=-xray
- ;;
- h8500hms)
- basic_machine=h8500-hitachi
- os=-hms
- ;;
- harris)
- basic_machine=m88k-harris
- os=-sysv3
- ;;
- hp300-*)
- basic_machine=m68k-hp
- ;;
- hp300bsd)
- basic_machine=m68k-hp
- os=-bsd
- ;;
- hp300hpux)
- basic_machine=m68k-hp
- os=-hpux
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
- basic_machine=hppa1.0-hp
+ cpu=hppa1.0
+ vendor=hp
;;
hp9k2[0-9][0-9] | hp9k31[0-9])
- basic_machine=m68000-hp
+ cpu=m68000
+ vendor=hp
;;
hp9k3[2-9][0-9])
- basic_machine=m68k-hp
+ cpu=m68k
+ vendor=hp
;;
hp9k6[0-9][0-9] | hp6[0-9][0-9])
- basic_machine=hppa1.0-hp
+ cpu=hppa1.0
+ vendor=hp
;;
hp9k7[0-79][0-9] | hp7[0-79][0-9])
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k78[0-9] | hp78[0-9])
# FIXME: really hppa2.0-hp
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
# FIXME: really hppa2.0-hp
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k8[0-9][13679] | hp8[0-9][13679])
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k8[0-9][0-9] | hp8[0-9][0-9])
- basic_machine=hppa1.0-hp
- ;;
- hppa-next)
- os=-nextstep3
+ cpu=hppa1.0
+ vendor=hp
;;
- hppaosf)
- basic_machine=hppa1.1-hp
- os=-osf
- ;;
- hppro)
- basic_machine=hppa1.1-hp
- os=-proelf
- ;;
- i370-ibm* | ibm*)
- basic_machine=i370-ibm
- ;;
-# I'm not sure what "Sysv32" means. Should this be sysv3.2?
i*86v32)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv32
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv32
;;
i*86v4*)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv4
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv4
;;
i*86v)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv
;;
i*86sol2)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-solaris2
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=solaris2
;;
- i386mach)
- basic_machine=i386-mach
- os=-mach
- ;;
- i386-vsta | vsta)
- basic_machine=i386-unknown
- os=-vsta
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ os=${os:-unicos}
;;
iris | iris4d)
- basic_machine=mips-sgi
+ cpu=mips
+ vendor=sgi
case $os in
- -irix*)
+ irix*)
;;
*)
- os=-irix4
+ os=irix4
;;
esac
;;
- isi68 | isi)
- basic_machine=m68k-isi
- os=-sysv
- ;;
- m88k-omron*)
- basic_machine=m88k-omron
- ;;
- magnum | m3230)
- basic_machine=mips-mips
- os=-sysv
- ;;
- merlin)
- basic_machine=ns32k-utek
- os=-sysv
- ;;
- mingw32)
- basic_machine=i386-pc
- os=-mingw32
- ;;
- mingw32ce)
- basic_machine=arm-unknown
- os=-mingw32ce
- ;;
miniframe)
- basic_machine=m68000-convergent
- ;;
- *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
- basic_machine=m68k-atari
- os=-mint
- ;;
- mipsEE* | ee | ps2)
- basic_machine=mips64r5900el-scei
- case $os in
- -linux*)
- ;;
- *)
- os=-elf
- ;;
- esac
- ;;
- iop)
- basic_machine=mipsel-scei
- os=-irx
- ;;
- dvp)
- basic_machine=dvp-scei
- os=-elf
- ;;
- mips3*-*)
- basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
- ;;
- mips3*)
- basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
- ;;
- monitor)
- basic_machine=m68k-rom68k
- os=-coff
- ;;
- morphos)
- basic_machine=powerpc-unknown
- os=-morphos
- ;;
- msdos)
- basic_machine=i386-pc
- os=-msdos
- ;;
- ms1-*)
- basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ cpu=m68000
+ vendor=convergent
;;
- mvs)
- basic_machine=i370-ibm
- os=-mvs
- ;;
- ncr3000)
- basic_machine=i486-ncr
- os=-sysv4
- ;;
- netbsd386)
- basic_machine=i386-unknown
- os=-netbsd
- ;;
- netwinder)
- basic_machine=armv4l-rebel
- os=-linux
- ;;
- news | news700 | news800 | news900)
- basic_machine=m68k-sony
- os=-newsos
- ;;
- news1000)
- basic_machine=m68030-sony
- os=-newsos
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ os=mint
;;
news-3600 | risc-news)
- basic_machine=mips-sony
- os=-newsos
- ;;
- necv70)
- basic_machine=v70-nec
- os=-sysv
+ cpu=mips
+ vendor=sony
+ os=newsos
;;
- next | m*-next )
- basic_machine=m68k-next
+ next | m*-next)
+ cpu=m68k
+ vendor=next
case $os in
- -nextstep* )
+ openstep*)
+ ;;
+ nextstep*)
;;
- -ns2*)
- os=-nextstep2
+ ns2*)
+ os=nextstep2
;;
*)
- os=-nextstep3
+ os=nextstep3
;;
esac
;;
- nh3000)
- basic_machine=m68k-harris
- os=-cxux
- ;;
- nh[45]000)
- basic_machine=m88k-harris
- os=-cxux
- ;;
- nindy960)
- basic_machine=i960-intel
- os=-nindy
- ;;
- mon960)
- basic_machine=i960-intel
- os=-mon960
- ;;
- nonstopux)
- basic_machine=mips-compaq
- os=-nonstopux
- ;;
np1)
- basic_machine=np1-gould
- ;;
- nsr-tandem)
- basic_machine=nsr-tandem
+ cpu=np1
+ vendor=gould
;;
op50n-* | op60c-*)
- basic_machine=hppa1.1-oki
- os=-proelf
- ;;
- openrisc | openrisc-*)
- basic_machine=or32-unknown
- ;;
- os400)
- basic_machine=powerpc-ibm
- os=-os400
- ;;
- OSE68000 | ose68000)
- basic_machine=m68000-ericsson
- os=-ose
- ;;
- os68k)
- basic_machine=m68k-none
- os=-os68k
+ cpu=hppa1.1
+ vendor=oki
+ os=proelf
;;
pa-hitachi)
- basic_machine=hppa1.1-hitachi
- os=-hiuxwe2
- ;;
- paragon)
- basic_machine=i860-intel
- os=-osf
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
;;
pbd)
- basic_machine=sparc-tti
+ cpu=sparc
+ vendor=tti
;;
pbb)
- basic_machine=m68k-tti
- ;;
- pc532 | pc532-*)
- basic_machine=ns32k-pc532
- ;;
- pc98)
- basic_machine=i386-pc
- ;;
- pc98-*)
- basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ cpu=m68k
+ vendor=tti
;;
- pentium | p5 | k5 | k6 | nexgen | viac3)
- basic_machine=i586-pc
- ;;
- pentiumpro | p6 | 6x86 | athlon | athlon_*)
- basic_machine=i686-pc
- ;;
- pentiumii | pentium2 | pentiumiii | pentium3)
- basic_machine=i686-pc
- ;;
- pentium4)
- basic_machine=i786-pc
- ;;
- pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
- basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentiumpro-* | p6-* | 6x86-* | athlon-*)
- basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
- basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentium4-*)
- basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ pc532)
+ cpu=ns32k
+ vendor=pc532
;;
pn)
- basic_machine=pn-gould
- ;;
- power) basic_machine=power-ibm
- ;;
- ppc) basic_machine=powerpc-unknown
+ cpu=pn
+ vendor=gould
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppcle | powerpclittle | ppc-le | powerpc-little)
- basic_machine=powerpcle-unknown
- ;;
- ppcle-* | powerpclittle-*)
- basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppc64) basic_machine=powerpc64-unknown
- ;;
- ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppc64le | powerpc64little | ppc64-le | powerpc64-little)
- basic_machine=powerpc64le-unknown
- ;;
- ppc64le-* | powerpc64little-*)
- basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ power)
+ cpu=power
+ vendor=ibm
;;
ps2)
- basic_machine=i386-ibm
- ;;
- pw32)
- basic_machine=i586-unknown
- os=-pw32
- ;;
- rdos)
- basic_machine=i386-pc
- os=-rdos
- ;;
- rom68k)
- basic_machine=m68k-rom68k
- os=-coff
+ cpu=i386
+ vendor=ibm
;;
rm[46]00)
- basic_machine=mips-siemens
+ cpu=mips
+ vendor=siemens
;;
rtpc | rtpc-*)
- basic_machine=romp-ibm
+ cpu=romp
+ vendor=ibm
;;
- s390 | s390-*)
- basic_machine=s390-ibm
- ;;
- s390x | s390x-*)
- basic_machine=s390x-ibm
- ;;
- sa29200)
- basic_machine=a29k-amd
- os=-udi
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ os=${os:-elf}
;;
- sb1)
- basic_machine=mipsisa64sb1-unknown
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ os=vxworks
;;
- sb1el)
- basic_machine=mipsisa64sb1el-unknown
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
;;
- sde)
- basic_machine=mipsisa32-sde
- os=-elf
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
;;
- sei)
- basic_machine=mips-sei
- os=-seiux
+ w65)
+ cpu=w65
+ vendor=wdc
;;
- sequent)
- basic_machine=i386-sequent
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ os=proelf
;;
- sh)
- basic_machine=sh-hitachi
- os=-hms
+ none)
+ cpu=none
+ vendor=none
;;
- sh5el)
- basic_machine=sh5le-unknown
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
;;
- sh64)
- basic_machine=sh64-unknown
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=`echo "$basic_machine" | sed 's/-.*//'`
;;
- sparclite-wrs | simso-wrs)
- basic_machine=sparclite-wrs
- os=-vxworks
+
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read cpu vendor <&2
- exit 1
+ # Recognize the canonical CPU types that are allowed with any
+ # company name.
+ case $cpu in
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | abacus \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+ | alphapca5[67] | alpha64pca5[67] \
+ | am33_2.0 \
+ | amdgcn \
+ | arc | arceb \
+ | arm | arm[lb]e | arme[lb] | armv* \
+ | avr | avr32 \
+ | asmjs \
+ | ba \
+ | be32 | be64 \
+ | bfin | bpf | bs2000 \
+ | c[123]* | c30 | [cjt]90 | c4x \
+ | c8051 | clipper | craynv | csky | cydra \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | elxsi | epiphany \
+ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | h8300 | h8500 \
+ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle \
+ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+ | m88110 | m88k | maxq | mb | mcore | mep | metag \
+ | microblaze | microblazeel \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64eb | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mmix \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nfp \
+ | nios | nios2 | nios2eb | nios2el \
+ | none | np1 | ns16k | ns32k | nvptx \
+ | open8 \
+ | or1k* \
+ | or32 \
+ | orion \
+ | picochip \
+ | pdp10 | pdp11 | pj | pjl | pn | power \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+ | pru \
+ | pyramid \
+ | riscv | riscv32 | riscv64 \
+ | rl78 | romp | rs6000 | rx \
+ | score \
+ | sh | shl \
+ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+ | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+ | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+ | spu \
+ | tahoe \
+ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+ | tron \
+ | ubicom32 \
+ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+ | vax \
+ | visium \
+ | w65 \
+ | wasm32 | wasm64 \
+ | we32k \
+ | x86 | x86_64 | xc16x | xgate | xps100 \
+ | xstormy16 | xtensa* \
+ | ymp \
+ | z8k | z80)
+ ;;
+
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ exit 1
+ ;;
+ esac
;;
esac
# Here we canonicalize certain aliases for manufacturers.
-case $basic_machine in
- *-digital*)
- basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+case $vendor in
+ digital*)
+ vendor=dec
;;
- *-commodore*)
- basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ commodore*)
+ vendor=cbm
;;
*)
;;
@@ -1196,190 +1278,246 @@ esac
# Decode manufacturer-specific aliases for certain operating systems.
-if [ x"$os" != x"" ]
+if [ x$os != x ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
- # -solaris* is a basic system type, with this one exception.
- -solaris1 | -solaris1.*)
- os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # solaris* is a basic system type, with this one exception.
+ auroraux)
+ os=auroraux
+ ;;
+ bluegene*)
+ os=cnk
;;
- -solaris)
- os=-solaris2
+ solaris1 | solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
- -svr4*)
- os=-sysv4
+ solaris)
+ os=solaris2
;;
- -unixware*)
- os=-sysv4.2uw
+ unixware*)
+ os=sysv4.2uw
;;
- -gnu/linux*)
+ gnu/linux*)
os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
;;
- # First accept the basic system types.
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
+ ;;
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
+ ;;
+ isc)
+ os=isc2.2
+ ;;
+ sco6)
+ os=sco5v6
+ ;;
+ sco5)
+ os=sco3.2v5
+ ;;
+ sco4)
+ os=sco3.2v4
+ ;;
+ sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ ;;
+ sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ scout)
+ # Don't match below
+ ;;
+ sco*)
+ os=sco3.2v2
+ ;;
+ psos*)
+ os=psos
+ ;;
+ # Now accept the basic system types.
# The portable systems comes first.
- # Each alternative MUST END IN A *, to match a version number.
- # -sysv* is not here because it comes later, after sysvr4.
- -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
- | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
- | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
- | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* \
- | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
- | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
- | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
- | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
- | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
- | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
- | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
- | -chorusos* | -chorusrdb* \
- | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
- | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
- | -uxpv* | -beos* | -mpeix* | -udk* \
- | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
- | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
- | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
- | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
- | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
- | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -irx*)
+ # Each alternative MUST end in a * to match a version number.
+ # sysv* is not here because it comes later, after sysvr4.
+ gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | kopensolaris* | plan9* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | knetbsd* | mirbsd* | netbsd* \
+ | bitrig* | openbsd* | solidbsd* | libertybsd* \
+ | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
+ | linux-newlib* | linux-musl* | linux-uclibc* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* \
+ | morphos* | superux* | rtmk* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
- -qnx*)
- case $basic_machine in
- x86-* | i*86-*)
+ qnx*)
+ case $cpu in
+ x86 | i*86)
;;
*)
- os=-nto$os
+ os=nto-$os
;;
esac
;;
- -nto-qnx*)
+ hiux*)
+ os=hiuxwe2
;;
- -nto*)
- os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ nto-qnx*)
;;
- -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
- | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
- | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
;;
- -mac*)
- os=`echo $os | sed -e 's|mac|macos|'`
+ sim | xray | os68k* | v88r* \
+ | windows* | osx | abug | netware* | os9* \
+ | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
;;
- -linux-dietlibc)
- os=-linux-dietlibc
+ linux-dietlibc)
+ os=linux-dietlibc
;;
- -linux*)
+ linux*)
os=`echo $os | sed -e 's|linux|linux-gnu|'`
;;
- -sunos5*)
- os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ lynx*178)
+ os=lynxos178
+ ;;
+ lynx*5)
+ os=lynxos5
;;
- -sunos6*)
- os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ lynx*)
+ os=lynxos
;;
- -opened*)
- os=-openedition
+ mac*)
+ os=`echo "$os" | sed -e 's|mac|macos|'`
;;
- -os400*)
- os=-os400
+ opened*)
+ os=openedition
;;
- -wince*)
- os=-wince
+ os400*)
+ os=os400
;;
- -osfrose*)
- os=-osfrose
+ sunos5*)
+ os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
;;
- -osf*)
- os=-osf
+ sunos6*)
+ os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
;;
- -utek*)
- os=-bsd
+ wince*)
+ os=wince
;;
- -dynix*)
- os=-bsd
+ utek*)
+ os=bsd
;;
- -acis*)
- os=-aos
+ dynix*)
+ os=bsd
;;
- -atheos*)
- os=-atheos
+ acis*)
+ os=aos
;;
- -syllable*)
- os=-syllable
+ atheos*)
+ os=atheos
;;
- -386bsd)
- os=-bsd
+ syllable*)
+ os=syllable
;;
- -ctix* | -uts*)
- os=-sysv
+ 386bsd)
+ os=bsd
+ ;;
+ ctix* | uts*)
+ os=sysv
;;
- -nova*)
- os=-rtmk-nova
+ nova*)
+ os=rtmk-nova
;;
- -ns2 )
- os=-nextstep2
+ ns2)
+ os=nextstep2
;;
- -nsk*)
- os=-nsk
+ nsk*)
+ os=nsk
;;
# Preserve the version number of sinix5.
- -sinix5.*)
+ sinix5.*)
os=`echo $os | sed -e 's|sinix|sysv|'`
;;
- -sinix*)
- os=-sysv4
+ sinix*)
+ os=sysv4
+ ;;
+ tpf*)
+ os=tpf
;;
- -tpf*)
- os=-tpf
+ triton*)
+ os=sysv3
;;
- -triton*)
- os=-sysv3
+ oss*)
+ os=sysv3
;;
- -oss*)
- os=-sysv3
+ svr4*)
+ os=sysv4
;;
- -svr4)
- os=-sysv4
+ svr3)
+ os=sysv3
;;
- -svr3)
- os=-sysv3
+ sysvr4)
+ os=sysv4
;;
- -sysvr4)
- os=-sysv4
+ # This must come after sysvr4.
+ sysv*)
;;
- # This must come after -sysvr4.
- -sysv*)
+ ose*)
+ os=ose
;;
- -ose*)
- os=-ose
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
;;
- -es1800*)
- os=-ose
+ zvmoe)
+ os=zvmoe
;;
- -xenix)
- os=-xenix
+ dicos*)
+ os=dicos
;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
- os=-mint
+ pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $cpu in
+ arm*)
+ os=eabi
+ ;;
+ *)
+ os=elf
+ ;;
+ esac
;;
- -aros*)
- os=-aros
+ nacl*)
;;
- -kaos*)
- os=-kaos
+ ios)
;;
- -zvmoe)
- os=-zvmoe
+ none)
;;
- -none)
+ *-eabi)
;;
*)
- # Get rid of the `-' at the beginning of $os.
- os=`echo $os | sed 's/[^-]*-//'`
- echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
exit 1
;;
esac
@@ -1395,249 +1533,265 @@ else
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
-case $basic_machine in
- score-*)
- os=-elf
+case $cpu-$vendor in
+ score-*)
+ os=elf
;;
- spu-*)
- os=-elf
+ spu-*)
+ os=elf
;;
*-acorn)
- os=-riscix1.2
+ os=riscix1.2
;;
arm*-rebel)
- os=-linux
+ os=linux
;;
arm*-semi)
- os=-aout
+ os=aout
+ ;;
+ c4x-* | tic4x-*)
+ os=coff
+ ;;
+ c8051-*)
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
;;
- c4x-* | tic4x-*)
- os=-coff
+ hexagon-*)
+ os=elf
+ ;;
+ tic54x-*)
+ os=coff
+ ;;
+ tic55x-*)
+ os=coff
+ ;;
+ tic6x-*)
+ os=coff
;;
# This must come before the *-dec entry.
pdp10-*)
- os=-tops20
+ os=tops20
;;
pdp11-*)
- os=-none
+ os=none
;;
*-dec | vax-*)
- os=-ultrix4.2
+ os=ultrix4.2
;;
m68*-apollo)
- os=-domain
+ os=domain
;;
i386-sun)
- os=-sunos4.0.2
+ os=sunos4.0.2
;;
m68000-sun)
- os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
+ os=sunos3
;;
m68*-cisco)
- os=-aout
+ os=aout
;;
- mep-*)
- os=-elf
+ mep-*)
+ os=elf
;;
mips*-cisco)
- os=-elf
+ os=elf
;;
mips*-*)
- os=-elf
+ os=elf
;;
or32-*)
- os=-coff
+ os=coff
;;
*-tti) # must be before sparc entry or we get the wrong os.
- os=-sysv3
+ os=sysv3
;;
sparc-* | *-sun)
- os=-sunos4.1.1
+ os=sunos4.1.1
;;
- *-be)
- os=-beos
+ pru-*)
+ os=elf
;;
- *-haiku)
- os=-haiku
+ *-be)
+ os=beos
;;
*-ibm)
- os=-aix
+ os=aix
;;
- *-knuth)
- os=-mmixware
+ *-knuth)
+ os=mmixware
;;
*-wec)
- os=-proelf
+ os=proelf
;;
*-winbond)
- os=-proelf
+ os=proelf
;;
*-oki)
- os=-proelf
+ os=proelf
;;
*-hp)
- os=-hpux
+ os=hpux
;;
*-hitachi)
- os=-hiux
+ os=hiux
;;
i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
- os=-sysv
+ os=sysv
;;
*-cbm)
- os=-amigaos
+ os=amigaos
;;
*-dg)
- os=-dgux
+ os=dgux
;;
*-dolphin)
- os=-sysv3
+ os=sysv3
;;
m68k-ccur)
- os=-rtu
+ os=rtu
;;
m88k-omron*)
- os=-luna
+ os=luna
;;
- *-next )
- os=-nextstep
+ *-next)
+ os=nextstep
;;
*-sequent)
- os=-ptx
+ os=ptx
;;
*-crds)
- os=-unos
+ os=unos
;;
*-ns)
- os=-genix
+ os=genix
;;
i370-*)
- os=-mvs
- ;;
- *-next)
- os=-nextstep3
+ os=mvs
;;
*-gould)
- os=-sysv
+ os=sysv
;;
*-highlevel)
- os=-bsd
+ os=bsd
;;
*-encore)
- os=-bsd
+ os=bsd
;;
*-sgi)
- os=-irix
+ os=irix
;;
*-siemens)
- os=-sysv4
+ os=sysv4
;;
*-masscomp)
- os=-rtu
+ os=rtu
;;
f30[01]-fujitsu | f700-fujitsu)
- os=-uxpv
+ os=uxpv
;;
*-rom68k)
- os=-coff
+ os=coff
;;
*-*bug)
- os=-coff
+ os=coff
;;
*-apple)
- os=-macos
+ os=macos
;;
*-atari*)
- os=-mint
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
;;
*)
- os=-none
+ os=none
;;
esac
fi
# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer. We pick the logical manufacturer.
-vendor=unknown
-case $basic_machine in
- *-unknown)
+case $vendor in
+ unknown)
case $os in
- -riscix*)
+ riscix*)
vendor=acorn
;;
- -sunos*)
+ sunos*)
vendor=sun
;;
- -aix*)
+ cnk*|-aix*)
vendor=ibm
;;
- -beos*)
+ beos*)
vendor=be
;;
- -hpux*)
+ hpux*)
vendor=hp
;;
- -mpeix*)
+ mpeix*)
vendor=hp
;;
- -hiux*)
+ hiux*)
vendor=hitachi
;;
- -unos*)
+ unos*)
vendor=crds
;;
- -dgux*)
+ dgux*)
vendor=dg
;;
- -luna*)
+ luna*)
vendor=omron
;;
- -genix*)
+ genix*)
vendor=ns
;;
- -mvs* | -opened*)
+ clix*)
+ vendor=intergraph
+ ;;
+ mvs* | opened*)
vendor=ibm
;;
- -os400*)
+ os400*)
vendor=ibm
;;
- -ptx*)
+ ptx*)
vendor=sequent
;;
- -tpf*)
+ tpf*)
vendor=ibm
;;
- -vxsim* | -vxworks* | -windiss*)
+ vxsim* | vxworks* | windiss*)
vendor=wrs
;;
- -aux*)
+ aux*)
vendor=apple
;;
- -hms*)
+ hms*)
vendor=hitachi
;;
- -mpw* | -macos*)
+ mpw* | macos*)
vendor=apple
;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
vendor=atari
;;
- -vos*)
+ vos*)
vendor=stratus
;;
esac
- basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
;;
esac
-echo $basic_machine$os
+echo "$cpu-$vendor-$os"
exit
# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
diff --git a/sqlite/configure b/sqlite/configure
index bf0cc3a4..4051931a 100755
--- a/sqlite/configure
+++ b/sqlite/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for sqlite 3.14.1.
+# Generated by GNU Autoconf 2.69 for sqlite 3.47.2.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
-PACKAGE_VERSION='3.14.1'
-PACKAGE_STRING='sqlite 3.14.1'
+PACKAGE_VERSION='3.47.2'
+PACKAGE_STRING='sqlite 3.47.2'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -770,26 +770,17 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
LIBOBJS
BUILD_CFLAGS
+AMALGAMATION_LINE_MACROS
USE_GCOV
OPT_FEATURE_FLAGS
+HAVE_ZLIB
USE_AMALGAMATION
TARGET_DEBUG
+TARGET_HAVE_LINENOISE
TARGET_HAVE_EDITLINE
TARGET_HAVE_READLINE
TARGET_READLINE_INC
TARGET_READLINE_LIBS
-HAVE_TCL
-TCL_SHLIB_SUFFIX
-TCL_STUB_LIB_SPEC
-TCL_STUB_LIB_FLAG
-TCL_STUB_LIB_FILE
-TCL_LIB_SPEC
-TCL_LIB_FLAG
-TCL_LIB_FILE
-TCL_INCLUDE_SPEC
-TCL_SRC_DIR
-TCL_BIN_DIR
-TCL_VERSION
TARGET_EXEEXT
SQLITE_OS_WIN
SQLITE_OS_UNIX
@@ -798,11 +789,16 @@ TEMP_STORE
ALLOWRELEASE
SQLITE_THREADSAFE
BUILD_CC
-VERSION_NUMBER
+HAVE_WASI_SDK
RELEASE
VERSION
program_prefix
+TSTRNNR_OPTS
TCLLIBDIR
+HAVE_TCL
+TCL_STUB_LIB_SPEC
+TCL_LIB_SPEC
+TCL_INCLUDE_SPEC
TCLSH_CMD
INSTALL_DATA
INSTALL_SCRIPT
@@ -891,24 +887,32 @@ enable_fast_install
with_gnu_ld
enable_libtool_lock
enable_largefile
+with_tclsh
+with_tcl
+enable_tcl
+enable_test_status
+with_wasi_sdk
enable_threadsafe
enable_releasemode
enable_tempstore
-enable_tcl
-with_tcl
enable_editline
enable_readline
with_readline_lib
with_readline_inc
+with_linenoise
enable_debug
enable_amalgamation
enable_load_extension
+enable_math
+enable_json
+enable_all
enable_memsys5
enable_memsys3
enable_fts3
enable_fts4
enable_fts5
-enable_json1
+enable_update_limit
+enable_geopoly
enable_rtree
enable_session
enable_gcov
@@ -1463,7 +1467,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures sqlite 3.14.1 to adapt to many kinds of systems.
+\`configure' configures sqlite 3.47.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1528,7 +1532,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sqlite 3.14.1:";;
+ short | recursive ) echo "Configuration of sqlite 3.47.2:";;
esac
cat <<\_ACEOF
@@ -1542,11 +1546,13 @@ Optional Features:
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
--disable-largefile omit support for large files
+ --disable-tcl omit building accessory programs that require
+ TCL-dev
+ --enable-test-status Full-screen status of tests
--disable-threadsafe Disable mutexing
--enable-releasemode Support libtool link to release mode
--enable-tempstore Use an in-ram database for temporary tables
(never,no,yes,always)
- --disable-tcl do not build TCL extension
--enable-editline enable BSD editline support
--disable-readline disable readline support
--enable-debug enable debugging & verbose explain
@@ -1554,12 +1560,16 @@ Optional Features:
separately
--disable-load-extension
Disable loading of external extensions
+ --disable-math Disable math functions
+ --disable-json Disable JSON functions
+ --enable-all Enable FTS4, FTS5, Geopoly, RTree, Sessions
--enable-memsys5 Enable MEMSYS5
--enable-memsys3 Enable MEMSYS3
--enable-fts3 Enable the FTS3 extension
--enable-fts4 Enable the FTS4 extension
--enable-fts5 Enable the FTS5 extension
- --enable-json1 Enable the JSON1 extension
+ --enable-update-limit Enable the UPDATE/DELETE LIMIT clause
+ --enable-geopoly Enable the GEOPOLY extension
--enable-rtree Enable the RTREE extension
--enable-session Enable the SESSION extension
--enable-gcov Enable coverage testing using gcov
@@ -1570,10 +1580,13 @@ Optional Packages:
--with-pic try to use only PIC/non-PIC objects [default=use
both]
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
- --with-tcl=DIR directory containing tcl configuration
- (tclConfig.sh)
+ --with-tclsh=PATHNAME full pathname of a tclsh to use
+ --with-tcl=DIR directory containing (tclConfig.sh)
+ --with-wasi-sdk=DIR directory containing the WASI SDK. Triggers
+ cross-compile to WASM.
--with-readline-lib specify readline library
--with-readline-inc specify readline include paths
+ --with-linenoise=DIR source directory for linenoise library
Some influential environment variables:
CC C compiler command
@@ -1652,7 +1665,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sqlite configure 3.14.1
+sqlite configure 3.47.2
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2071,7 +2084,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by sqlite $as_me 3.14.1, which was
+It was created by sqlite $as_me 3.47.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3929,13 +3942,13 @@ if ${lt_cv_nm_interface+:} false; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
- (eval echo "\"\$as_me:3932: $ac_compile\"" >&5)
+ (eval echo "\"\$as_me:3945: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
- (eval echo "\"\$as_me:3935: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval echo "\"\$as_me:3948: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
- (eval echo "\"\$as_me:3938: output\"" >&5)
+ (eval echo "\"\$as_me:3951: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@@ -5141,7 +5154,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5144 "configure"' > conftest.$ac_ext
+ echo '#line 5157 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -6666,11 +6679,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:6669: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6682: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6673: \$? = $ac_status" >&5
+ echo "$as_me:6686: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7005,11 +7018,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7008: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7021: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:7012: \$? = $ac_status" >&5
+ echo "$as_me:7025: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7110,11 +7123,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7113: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7126: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7117: \$? = $ac_status" >&5
+ echo "$as_me:7130: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -7165,11 +7178,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7168: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7181: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7172: \$? = $ac_status" >&5
+ echo "$as_me:7185: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -9545,7 +9558,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9548 "configure"
+#line 9561 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9641,7 +9654,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9644 "configure"
+#line 9657 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -10298,11 +10311,31 @@ done
USE_AMALGAMATION=1
#########
-# See whether we can run specific tclsh versions known to work well;
-# if not, then we fall back to plain tclsh.
-# TODO: try other versions before falling back?
+# Figure out all the name of a working tclsh and parameters needed to compile against Tcl.
+# The --with-tcl= and/or --with-tclsh= configuration arguments might be useful for this.
#
-for ac_prog in tclsh8.6 tclsh8.5 tclsh
+
+# Check whether --with-tclsh was given.
+if test "${with_tclsh+set}" = set; then :
+ withval=$with_tclsh;
+fi
+
+
+# Check whether --with-tcl was given.
+if test "${with_tcl+set}" = set; then :
+ withval=$with_tcl;
+fi
+
+# Check whether --enable-tcl was given.
+if test "${enable_tcl+set}" = set; then :
+ enableval=$enable_tcl; use_tcl=$enableval
+else
+ use_tcl=yes
+fi
+
+original_use_tcl=${use_tcl}
+if test x"${with_tclsh}" = x -a x"${with_tcl}" = x; then
+ for ac_prog in tclsh9.0 tclsh8.6 tclsh
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
@@ -10345,10 +10378,99 @@ fi
done
test -n "$TCLSH_CMD" || TCLSH_CMD="none"
+ with_tclsh=${TCLSH_CMD}
+fi
+if test x"${with_tclsh}" != x -a x"${with_tclsh}" != xnone; then
+ TCLSH_CMD=${with_tclsh}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using tclsh at \"$TCLSH_CMD\"" >&5
+$as_echo "using tclsh at \"$TCLSH_CMD\"" >&6; }
+ if test x"${use_tcl}" = "xyes"; then
+ with_tcl=`${with_tclsh} <${srcdir}/tool/find_tclconfig.tcl`
+ if test x"${with_tcl}" != x; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TCLSH_CMD recommends the tclConfig.sh at ${with_tcl}" >&5
+$as_echo "$TCLSH_CMD recommends the tclConfig.sh at ${with_tcl}" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $TCLSH_CMD is unable to recommend a tclConfig.sh" >&5
+$as_echo "$as_me: WARNING: $TCLSH_CMD is unable to recommend a tclConfig.sh" >&2;}
+ use_tcl=no
+ fi
+ fi
+fi
+if test x"${use_tcl}" = "xyes"; then
+ if test x"${with_tcl}" != x; then
+ if test -r ${with_tcl}/tclConfig.sh; then
+ tclconfig="${with_tcl}/tclConfig.sh"
+ else
+ for i in tcl8.6 tcl9.0 lib; do
+ if test -r ${with_tcl}/$i/tclConfig.sh; then
+ tclconfig=${with_tcl}/$i/tclConfig.sh
+ break
+ fi
+ done
+ fi
+ if test ! -r "${tclconfig}"; then
+ as_fn_error $? "no tclConfig.sh file found under ${with_tcl}" "$LINENO" 5
+ fi
+ else
+ # If we have not yet found a tclConfig.sh file, look in $libdir whic is
+ # set automatically by autoconf or by the --prefix command-line option.
+ # See https://sqlite.org/forum/forumpost/e04e693439a22457
+ libdir=${prefix}/lib
+ if test -r ${libdir}/tclConfig.sh; then
+ tclconfig=${libdir}/tclConfig.sh
+ else
+ for i in tcl8.6 tcl9.0 lib; do
+ if test -r ${libdir}/$i/tclConfig.sh; then
+ tclconfig=${libdir}/$i/tclConfig.sh
+ break
+ fi
+ done
+ fi
+ if test ! -r "${tclconfig}"; then
+ as_fn_error $? "cannot find a usable tclConfig.sh file.
+ Use --with-tcl=DIR to specify a directory where tclConfig.sh can be found.
+ SQLite does not use TCL internally, but TCL is required to build SQLite
+ from canonical sources and TCL is required for testing." "$LINENO" 5
+ fi
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: loading TCL configuration from ${tclconfig}" >&5
+$as_echo "loading TCL configuration from ${tclconfig}" >&6; }
+ . ${tclconfig}
+
+
+
+ # There are lots of other configuration variables that are provided by the
+ # tclConfig.sh file and that could be included here. But as of right now,
+ # TCL_LIB_SPEC is the only what that the Makefile uses.
+ HAVE_TCL=1
+elif test x"${original_use_tcl}" = "xno"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unable to run tests because of --disable-tcl" >&5
+$as_echo "unable to run tests because of --disable-tcl" >&6; }
+ HAVE_TCL=0
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unable to run tests because no tclConfig.sh file could be located" >&5
+$as_echo "unable to run tests because no tclConfig.sh file could be located" >&6; }
+ HAVE_TCL=0
+fi
+
+if test x"$TCLSH_CMD" = x; then
+ TCLSH_CMD=${TCL_EXEC_PREFIX}/bin/tclsh${TCL_VERSION}
+ if test ! -x ${TCLSH_CMD}; then
+ TCLSH_CMD_2=${TCL_EXEC_PREFIX}/bin/tclsh
+ if test ! -x ${TCLSH_CMD_2}; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find a usable tclsh at either ${TCLSH_CMD} or ${TCLSH_CMD_2}" >&5
+$as_echo "$as_me: WARNING: cannot find a usable tclsh at either ${TCLSH_CMD} or ${TCLSH_CMD_2}" >&2;}
+ TCLSH_CMD=none
+ else
+ TCLSH_CMD=${TCLSH_CMD_2}
+ fi
+ fi
+fi
if test "$TCLSH_CMD" = "none"; then
# If we can't find a local tclsh, then building the amalgamation will fail.
# We act as though --disable-amalgamation has been used.
- echo "Warning: can't find tclsh - defaulting to non-amalgamation build."
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Warning: can't find tclsh - defaulting to non-amalgamation build." >&5
+$as_echo "$as_me: WARNING: Warning: can't find tclsh - defaulting to non-amalgamation build." >&2;}
USE_AMALGAMATION=0
TCLSH_CMD="tclsh"
fi
@@ -10356,14 +10478,32 @@ fi
if test "x${TCLLIBDIR+set}" != "xset" ; then
- TCLLIBDIR='$(libdir)'
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
- TCLLIBDIR=$i
- break
+ if test -d $i ; then
+ TCLLIBDIR=$i
+ break
+ fi
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
+#########
+# Set up options for running tests.
+#
+# Check whether --enable-test-status was given.
+if test "${enable_test_status+set}" = set; then :
+ enableval=$enable_test_status; use_vt100=$enableval
+else
+ use_vt100=no
+fi
+
+if test $use_vt100 != no; then
+ TSTRNNR_OPTS=--status
+else
+ TSTRNNR_OPTS=
+fi
+
+
#########
# Set up an appropriate program prefix
@@ -10381,11 +10521,67 @@ RELEASE=`cat $srcdir/VERSION`
{ $as_echo "$as_me:${as_lineno-$LINENO}: Release set to $RELEASE" >&5
$as_echo "$as_me: Release set to $RELEASE" >&6;}
-VERSION_NUMBER=`cat $srcdir/VERSION \
- | sed 's/[^0-9]/ /g' \
- | awk '{printf "%d%03d%03d",$1,$2,$3}'`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: Version number set to $VERSION_NUMBER" >&5
-$as_echo "$as_me: Version number set to $VERSION_NUMBER" >&6;}
+
+##########
+# Handle --with-wasi-sdk=DIR
+#
+# This must be early because it changes the toolchain.
+#
+
+# Check whether --with-wasi-sdk was given.
+if test "${with_wasi_sdk+set}" = set; then :
+ withval=$with_wasi_sdk; with_wasisdk=${withval}
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for WASI SDK directory" >&5
+$as_echo_n "checking for WASI SDK directory... " >&6; }
+if ${ac_cv_c_wasi_sdk+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # First check to see if --with-tcl was specified.
+ if test x"${with_wasi_sdk}" != x ; then
+ if ! test -d "${with_wasi_sdk}" ; then
+ as_fn_error $? "${with_wasi_sdk} directory doesn't exist" "$LINENO" 5
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_wasi_sdk}: using wasi-sdk clang, disabling: tcl, CLI shell, DLL" >&5
+$as_echo "${with_wasi_sdk}: using wasi-sdk clang, disabling: tcl, CLI shell, DLL" >&6; }
+ use_wasi_sdk=yes
+ else
+ use_wasi_sdk=no
+ fi
+
+fi
+
+if test "${use_wasi_sdk}" = "no" ; then
+ HAVE_WASI_SDK=""
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+ HAVE_WASI_SDK=1
+# Changing --host and --target have no effect here except to possibly
+# cause confusion. autoconf has finished processing them by this
+# point.
+#
+# host_alias=wasm32-wasi
+# target=wasm32-wasi
+#
+# Merely changing CC and LD to the wasi-sdk's is enough to get
+# sqlite3.o building in WASM format.
+ CC="${with_wasi_sdk}/bin/clang"
+ LD="${with_wasi_sdk}/bin/wasm-ld"
+ RANLIB="${with_wasi_sdk}/bin/llvm-ranlib"
+ cross_compiling=yes
+ enable_threadsafe=no
+ use_tcl=no
+ enable_tcl=no
+ # libtool is apparently hard-coded to use gcc for linking DLLs, so
+ # we disable the DLL build...
+ enable_shared=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+
#########
@@ -10452,8 +10648,6 @@ fi
# Check whether --enable-threadsafe was given.
if test "${enable_threadsafe+set}" = set; then :
enableval=$enable_threadsafe;
-else
- enable_threadsafe=yes
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support threadsafe operation" >&5
@@ -10697,213 +10891,6 @@ fi
-##########
-# Figure out all the parameters needed to compile against Tcl.
-#
-# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
-# macros in the in the tcl.m4 file of the standard TCL distribution.
-# Those macros could not be used directly since we have to make some
-# minor changes to accomodate systems that do not have TCL installed.
-#
-# Check whether --enable-tcl was given.
-if test "${enable_tcl+set}" = set; then :
- enableval=$enable_tcl; use_tcl=$enableval
-else
- use_tcl=yes
-fi
-
-if test "${use_tcl}" = "yes" ; then
-
-# Check whether --with-tcl was given.
-if test "${with_tcl+set}" = set; then :
- withval=$with_tcl; with_tclconfig=${withval}
-fi
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl configuration" >&5
-$as_echo_n "checking for Tcl configuration... " >&6; }
- if ${ac_cv_c_tclconfig+:} false; then :
- $as_echo_n "(cached) " >&6
-else
-
- # First check to see if --with-tcl was specified.
- if test x"${with_tclconfig}" != x ; then
- if test -f "${with_tclconfig}/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
- else
- as_fn_error $? "${with_tclconfig} directory doesn't contain tclConfig.sh" "$LINENO" 5
- fi
- fi
-
- # Start autosearch by asking tclsh
- if test x"${ac_cv_c_tclconfig}" = x ; then
- if test x"$cross_compiling" = xno; then
- for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}`
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig="$i"
- break
- fi
- done
- fi
- fi
-
- # On ubuntu 14.10, $auto_path on tclsh is not quite correct.
- # So try again after applying corrections.
- if test x"${ac_cv_c_tclconfig}" = x ; then
- if test x"$cross_compiling" = xno; then
- for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD} | sed 's,/tcltk/tcl,/tcl,g'`
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig="$i"
- break
- fi
- done
- fi
- fi
-
- # Recent versions of Xcode on Macs hid the tclConfig.sh file
- # in a strange place.
- if test x"${ac_cv_c_tclconfig}" = x ; then
- if test x"$cross_compiling" = xno; then
- for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig="$i"
- break
- fi
- done
- fi
- fi
-
- # then check for a private Tcl installation
- if test x"${ac_cv_c_tclconfig}" = x ; then
- for i in \
- ../tcl \
- `ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
- `ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \
- `ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \
- ../../tcl \
- `ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
- `ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \
- `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \
- ../../../tcl \
- `ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
- `ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \
- `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null`
- do
- if test -f "$i/unix/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
- break
- fi
- done
- fi
-
- # check in a few common install locations
- if test x"${ac_cv_c_tclconfig}" = x ; then
- for i in \
- `ls -d ${libdir} 2>/dev/null` \
- `ls -d /usr/local/lib 2>/dev/null` \
- `ls -d /usr/contrib/lib 2>/dev/null` \
- `ls -d /usr/lib 2>/dev/null`
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd $i; pwd)`
- break
- fi
- done
- fi
-
- # check in a few other private locations
- if test x"${ac_cv_c_tclconfig}" = x ; then
- for i in \
- ${srcdir}/../tcl \
- `ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
- `ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \
- `ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null`
- do
- if test -f "$i/unix/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
- break
- fi
- done
- fi
-
-fi
-
-
- if test x"${ac_cv_c_tclconfig}" = x ; then
- use_tcl=no
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Can't find Tcl configuration definitions" >&5
-$as_echo "$as_me: WARNING: Can't find Tcl configuration definitions" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Without Tcl the regression tests cannot be executed ***" >&5
-$as_echo "$as_me: WARNING: *** Without Tcl the regression tests cannot be executed ***" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Consider using --with-tcl=... to define location of Tcl ***" >&5
-$as_echo "$as_me: WARNING: *** Consider using --with-tcl=... to define location of Tcl ***" >&2;}
- else
- TCL_BIN_DIR=${ac_cv_c_tclconfig}
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: found $TCL_BIN_DIR/tclConfig.sh" >&5
-$as_echo "found $TCL_BIN_DIR/tclConfig.sh" >&6; }
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for existence of $TCL_BIN_DIR/tclConfig.sh" >&5
-$as_echo_n "checking for existence of $TCL_BIN_DIR/tclConfig.sh... " >&6; }
- if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: loading" >&5
-$as_echo "loading" >&6; }
- . $TCL_BIN_DIR/tclConfig.sh
- else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5
-$as_echo "file not found" >&6; }
- fi
-
- #
- # If the TCL_BIN_DIR is the build directory (not the install directory),
- # then set the common variable name to the value of the build variables.
- # For example, the variable TCL_LIB_SPEC will be set to the value
- # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
- # instead of TCL_BUILD_LIB_SPEC since it will work with both an
- # installed and uninstalled version of Tcl.
- #
-
- if test -f $TCL_BIN_DIR/Makefile ; then
- TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
- TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
- TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
- fi
-
- #
- # eval is required to do the TCL_DBGX substitution
- #
-
- eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
- eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
- eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
-
- eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
- eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
- eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- fi
-fi
-if test "${use_tcl}" = "no" ; then
- HAVE_TCL=""
-else
- HAVE_TCL=1
-fi
-
-
##########
# Figure out what C libraries are required to compile programs
# that use "readline()" library.
@@ -11176,6 +11163,27 @@ fi
fi
fi
+# Check whether --with-linenoise was given.
+if test "${with_linenoise+set}" = set; then :
+ withval=$with_linenoise; with_linenoise=$withval
+else
+ with_linenoise="no"
+fi
+
+if test "x$with_linenoise" != "xno"; then
+ TARGET_HAVE_READLINE=0
+ TARGET_HAVE_EDITLINE=0
+ TARGET_HAVE_LINENOISE=1
+ TARGET_READLINE_INC="-I${with_linenoise}"
+ TARGET_READLINE_LIBS="${with_linenoise}/linenoise.c"
+ echo "using linenoise source code at ${with_linenoise}"
+else
+ TARGET_HAVE_LINENOISE=0
+ echo "not using linenoise"
+fi
+
+
+
@@ -11246,42 +11254,119 @@ fi
# check for debug enabled
# Check whether --enable-debug was given.
if test "${enable_debug+set}" = set; then :
- enableval=$enable_debug; use_debug=$enableval
-else
- use_debug=no
+ enableval=$enable_debug;
fi
-if test "${use_debug}" = "yes" ; then
- TARGET_DEBUG="-DSQLITE_DEBUG=1"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build type" >&5
+$as_echo_n "checking build type... " >&6; }
+if test "${enable_debug}" = "yes" ; then
+ TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: debug" >&5
+$as_echo "debug" >&6; }
else
TARGET_DEBUG="-DNDEBUG"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: release" >&5
+$as_echo "release" >&6; }
fi
#########
# See whether we should use the amalgamation to build
+
# Check whether --enable-amalgamation was given.
if test "${enable_amalgamation+set}" = set; then :
- enableval=$enable_amalgamation; use_amalgamation=$enableval
-else
- use_amalgamation=yes
+ enableval=$enable_amalgamation;
fi
-if test "${use_amalgamation}" != "yes" ; then
+if test "${enable_amalgamation}" = "no" ; then
USE_AMALGAMATION=0
fi
+#########
+# Look for zlib. Only needed by extensions and by the sqlite3.exe shell
+for ac_header in zlib.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_zlib_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_ZLIB_H 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing deflate" >&5
+$as_echo_n "checking for library containing deflate... " >&6; }
+if ${ac_cv_search_deflate+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char deflate ();
+int
+main ()
+{
+return deflate ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' z; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_deflate=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_deflate+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_deflate+:} false; then :
+
+else
+ ac_cv_search_deflate=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_deflate" >&5
+$as_echo "$ac_cv_search_deflate" >&6; }
+ac_res=$ac_cv_search_deflate
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ HAVE_ZLIB="-DSQLITE_HAVE_ZLIB=1"
+else
+ HAVE_ZLIB=""
+fi
+
+
+
#########
# See whether we should allow loadable extensions
# Check whether --enable-load-extension was given.
if test "${enable_load_extension+set}" = set; then :
- enableval=$enable_load_extension; use_loadextension=$enableval
+ enableval=$enable_load_extension;
else
- use_loadextension=yes
+ enable_load_extension=yes
fi
-if test "${use_loadextension}" = "yes" ; then
+if test "${enable_load_extension}" = "yes" ; then
OPT_FEATURE_FLAGS=""
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
$as_echo_n "checking for library containing dlopen... " >&6; }
@@ -11343,20 +11428,121 @@ else
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
+##########
+# Do we want to support math functions
+#
+# Check whether --enable-math was given.
+if test "${enable_math+set}" = set; then :
+ enableval=$enable_math;
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support math functions" >&5
+$as_echo_n "checking whether to support math functions... " >&6; }
+if test "$enable_math" = "no"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MATH_FUNCTIONS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ceil" >&5
+$as_echo_n "checking for library containing ceil... " >&6; }
+if ${ac_cv_search_ceil+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ceil ();
+int
+main ()
+{
+return ceil ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' m; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ceil=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ceil+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ceil+:} false; then :
+
+else
+ ac_cv_search_ceil=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ceil" >&5
+$as_echo "$ac_cv_search_ceil" >&6; }
+ac_res=$ac_cv_search_ceil
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+##########
+# Do we want to support JSON functions
+#
+# Check whether --enable-json was given.
+if test "${enable_json+set}" = set; then :
+ enableval=$enable_json;
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support JSON functions" >&5
+$as_echo_n "checking whether to support JSON functions... " >&6; }
+if test "$enable_json" = "no"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_OMIT_JSON"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+
+########
+# The --enable-all argument is short-hand to enable
+# multiple extensions.
+# Check whether --enable-all was given.
+if test "${enable_all+set}" = set; then :
+ enableval=$enable_all;
+fi
+
+
##########
# Do we want to support memsys3 and/or memsys5
#
# Check whether --enable-memsys5 was given.
if test "${enable_memsys5+set}" = set; then :
- enableval=$enable_memsys5; enable_memsys5=yes
-else
- enable_memsys5=no
+ enableval=$enable_memsys5;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5
$as_echo_n "checking whether to support MEMSYS5... " >&6; }
if test "${enable_memsys5}" = "yes"; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS5"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS5"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
@@ -11365,15 +11551,13 @@ $as_echo "no" >&6; }
fi
# Check whether --enable-memsys3 was given.
if test "${enable_memsys3+set}" = set; then :
- enableval=$enable_memsys3; enable_memsys3=yes
-else
- enable_memsys3=no
+ enableval=$enable_memsys3;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5
$as_echo_n "checking whether to support MEMSYS3... " >&6; }
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS3"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS3"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
@@ -11385,23 +11569,30 @@ fi
# See whether we should enable Full Text Search extensions
# Check whether --enable-fts3 was given.
if test "${enable_fts3+set}" = set; then :
- enableval=$enable_fts3; enable_fts3=yes
-else
- enable_fts3=no
+ enableval=$enable_fts3;
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support FTS3" >&5
+$as_echo_n "checking whether to support FTS3... " >&6; }
if test "${enable_fts3}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS3"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
# Check whether --enable-fts4 was given.
if test "${enable_fts4+set}" = set; then :
- enableval=$enable_fts4; enable_fts4=yes
-else
- enable_fts4=no
+ enableval=$enable_fts4;
fi
-if test "${enable_fts4}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS4"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support FTS4" >&5
+$as_echo_n "checking whether to support FTS4... " >&6; }
+if test "${enable_fts4}" = "yes" -o "${enable_all}" = "yes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
$as_echo_n "checking for library containing log... " >&6; }
if ${ac_cv_search_log+:} false; then :
@@ -11458,16 +11649,21 @@ if test "$ac_res" != no; then :
fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
# Check whether --enable-fts5 was given.
if test "${enable_fts5+set}" = set; then :
- enableval=$enable_fts5; enable_fts5=yes
-else
- enable_fts5=no
+ enableval=$enable_fts5;
fi
-if test "${enable_fts5}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS5"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support FTS5" >&5
+$as_echo_n "checking whether to support FTS5... " >&6; }
+if test "${enable_fts5}" = "yes" -o "${enable_all}" = "yes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
$as_echo_n "checking for library containing log... " >&6; }
if ${ac_cv_search_log+:} false; then :
@@ -11524,50 +11720,90 @@ if test "$ac_res" != no; then :
fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
#########
-# See whether we should enable JSON1
-# Check whether --enable-json1 was given.
-if test "${enable_json1+set}" = set; then :
- enableval=$enable_json1; enable_json1=yes
+# See whether we should enable the LIMIT clause on UPDATE and DELETE
+# statements.
+# Check whether --enable-update-limit was given.
+if test "${enable_update_limit+set}" = set; then :
+ enableval=$enable_update_limit;
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support LIMIT on UPDATE and DELETE statements" >&5
+$as_echo_n "checking whether to support LIMIT on UPDATE and DELETE statements... " >&6; }
+if test "${enable_update_limit}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
else
- enable_json1=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
-if test "${enable_json1}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_JSON1"
+#########
+# See whether we should enable GEOPOLY
+# Check whether --enable-geopoly was given.
+if test "${enable_geopoly+set}" = set; then :
+ enableval=$enable_geopoly; enable_geopoly=yes
+else
+ enable_geopoly=no
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support GEOPOLY" >&5
+$as_echo_n "checking whether to support GEOPOLY... " >&6; }
+if test "${enable_geopoly}" = "yes" -o "${enable_all}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
+ enable_rtree=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
#########
# See whether we should enable RTREE
# Check whether --enable-rtree was given.
if test "${enable_rtree+set}" = set; then :
- enableval=$enable_rtree; enable_rtree=yes
-else
- enable_rtree=no
+ enableval=$enable_rtree;
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support RTREE" >&5
+$as_echo_n "checking whether to support RTREE... " >&6; }
if test "${enable_rtree}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_RTREE"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
#########
# See whether we should enable the SESSION extension
# Check whether --enable-session was given.
if test "${enable_session+set}" = set; then :
- enableval=$enable_session; enable_session=yes
-else
- enable_session=no
+ enableval=$enable_session;
fi
-if test "${enable_session}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_SESSION"
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_PREUPDATE_HOOK"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support SESSION" >&5
+$as_echo_n "checking whether to support SESSION... " >&6; }
+if test "${enable_session}" = "yes" -o "${enable_all}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
#########
-# attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter
+# attempt to duplicate any OMITS and ENABLES into the ${OPT_FEATURE_FLAGS} parameter
for option in $CFLAGS $CPPFLAGS
do
case $option in
@@ -11621,9 +11857,7 @@ BUILD_CFLAGS=$ac_temp_BUILD_CFLAGS
# See whether we should use GCOV
# Check whether --enable-gcov was given.
if test "${enable_gcov+set}" = set; then :
- enableval=$enable_gcov; use_gcov=$enableval
-else
- use_gcov=no
+ enableval=$enable_gcov;
fi
if test "${use_gcov}" = "yes" ; then
@@ -11633,10 +11867,21 @@ else
fi
+#########
+# Enable/disabled amalagamation line macros
+########
+AMALGAMATION_LINE_MACROS=--linemacros=0
+if test "${amalgamation_line_macros}" = "yes" ; then
+ AMALGAMATION_LINE_MACROS=--linemacros=1
+fi
+if test "${amalgamation_line_macros}" = "no" ; then
+ AMALGAMATION_LINE_MACROS=--linemacros=0
+fi
+
#########
# Output the config header
-ac_config_headers="$ac_config_headers config.h"
+ac_config_headers="$ac_config_headers sqlite_cfg.h"
#########
@@ -12151,7 +12396,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by sqlite $as_me 3.14.1, which was
+This file was extended by sqlite $as_me 3.47.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12217,7 +12462,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-sqlite config.status 3.14.1
+sqlite config.status 3.47.2
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -12599,7 +12844,7 @@ for ac_config_target in $ac_config_targets
do
case $ac_config_target in
"libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
- "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+ "sqlite_cfg.h") CONFIG_HEADERS="$CONFIG_HEADERS sqlite_cfg.h" ;;
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"sqlite3.pc") CONFIG_FILES="$CONFIG_FILES sqlite3.pc" ;;
diff --git a/sqlite/configure.ac b/sqlite/configure.ac
index c9e3af92..9a6eb1d1 100644
--- a/sqlite/configure.ac
+++ b/sqlite/configure.ac
@@ -70,11 +70,11 @@
# target platform. "" for Unix and ".exe" for windows.
#
# This configure.in file is easy to reuse on other projects. Just
-# change the argument to AC_INIT(). And disable any features that
+# change the argument to AC_INIT. And disable any features that
# you don't need (for example BLT) by erasing or commenting out
# the corresponding code.
#
-AC_INIT(sqlite, m4_esyscmd([cat VERSION | tr -d '\n']))
+AC_INIT([sqlite],m4_esyscmd(cat VERSION | tr -d '\n'))
dnl Make sure the local VERSION file matches this configure script
sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'`
@@ -88,7 +88,7 @@ fi
#########
# Programs needed
#
-AC_PROG_LIBTOOL
+LT_INIT
AC_PROG_INSTALL
#########
@@ -116,15 +116,101 @@ AC_CHECK_FUNCS([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_s
USE_AMALGAMATION=1
#########
-# See whether we can run specific tclsh versions known to work well;
-# if not, then we fall back to plain tclsh.
-# TODO: try other versions before falling back?
-#
-AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.6 tclsh8.5 tclsh], none)
+# Figure out all the name of a working tclsh and parameters needed to compile against Tcl.
+# The --with-tcl= and/or --with-tclsh= configuration arguments might be useful for this.
+#
+AC_ARG_WITH(tclsh, AS_HELP_STRING([--with-tclsh=PATHNAME],[full pathname of a tclsh to use]))
+AC_ARG_WITH(tcl, AS_HELP_STRING([--with-tcl=DIR],[directory containing (tclConfig.sh)]))
+AC_ARG_ENABLE(tcl, AS_HELP_STRING([--disable-tcl],[omit building accessory programs that require TCL-dev]),
+ [use_tcl=$enableval],[use_tcl=yes])
+original_use_tcl=${use_tcl}
+if test x"${with_tclsh}" = x -a x"${with_tcl}" = x; then
+ AC_CHECK_PROGS(TCLSH_CMD, [tclsh9.0 tclsh8.6 tclsh],none)
+ with_tclsh=${TCLSH_CMD}
+fi
+if test x"${with_tclsh}" != x -a x"${with_tclsh}" != xnone; then
+ TCLSH_CMD=${with_tclsh}
+ AC_MSG_RESULT([using tclsh at "$TCLSH_CMD"])
+ if test x"${use_tcl}" = "xyes"; then
+ with_tcl=`${with_tclsh} <${srcdir}/tool/find_tclconfig.tcl`
+ if test x"${with_tcl}" != x; then
+ AC_MSG_RESULT([$TCLSH_CMD recommends the tclConfig.sh at ${with_tcl}])
+ else
+ AC_MSG_WARN([$TCLSH_CMD is unable to recommend a tclConfig.sh])
+ use_tcl=no
+ fi
+ fi
+fi
+if test x"${use_tcl}" = "xyes"; then
+ if test x"${with_tcl}" != x; then
+ if test -r ${with_tcl}/tclConfig.sh; then
+ tclconfig="${with_tcl}/tclConfig.sh"
+ else
+ for i in tcl8.6 tcl9.0 lib; do
+ if test -r ${with_tcl}/$i/tclConfig.sh; then
+ tclconfig=${with_tcl}/$i/tclConfig.sh
+ break
+ fi
+ done
+ fi
+ if test ! -r "${tclconfig}"; then
+ AC_MSG_ERROR([no tclConfig.sh file found under ${with_tcl}])
+ fi
+ else
+ # If we have not yet found a tclConfig.sh file, look in $libdir whic is
+ # set automatically by autoconf or by the --prefix command-line option.
+ # See https://sqlite.org/forum/forumpost/e04e693439a22457
+ libdir=${prefix}/lib
+ if test -r ${libdir}/tclConfig.sh; then
+ tclconfig=${libdir}/tclConfig.sh
+ else
+ for i in tcl8.6 tcl9.0 lib; do
+ if test -r ${libdir}/$i/tclConfig.sh; then
+ tclconfig=${libdir}/$i/tclConfig.sh
+ break
+ fi
+ done
+ fi
+ if test ! -r "${tclconfig}"; then
+ AC_MSG_ERROR([cannot find a usable tclConfig.sh file.
+ Use --with-tcl=DIR to specify a directory where tclConfig.sh can be found.
+ SQLite does not use TCL internally, but TCL is required to build SQLite
+ from canonical sources and TCL is required for testing.])
+ fi
+ fi
+ AC_MSG_RESULT([loading TCL configuration from ${tclconfig}])
+ . ${tclconfig}
+ AC_SUBST(TCL_INCLUDE_SPEC)
+ AC_SUBST(TCL_LIB_SPEC)
+ AC_SUBST(TCL_STUB_LIB_SPEC)
+ # There are lots of other configuration variables that are provided by the
+ # tclConfig.sh file and that could be included here. But as of right now,
+ # TCL_LIB_SPEC is the only what that the Makefile uses.
+ HAVE_TCL=1
+elif test x"${original_use_tcl}" = "xno"; then
+ AC_MSG_RESULT([unable to run tests because of --disable-tcl])
+ HAVE_TCL=0
+else
+ AC_MSG_RESULT([unable to run tests because no tclConfig.sh file could be located])
+ HAVE_TCL=0
+fi
+AC_SUBST(HAVE_TCL)
+if test x"$TCLSH_CMD" = x; then
+ TCLSH_CMD=${TCL_EXEC_PREFIX}/bin/tclsh${TCL_VERSION}
+ if test ! -x ${TCLSH_CMD}; then
+ TCLSH_CMD_2=${TCL_EXEC_PREFIX}/bin/tclsh
+ if test ! -x ${TCLSH_CMD_2}; then
+ AC_MSG_WARN([cannot find a usable tclsh at either ${TCLSH_CMD} or ${TCLSH_CMD_2}])
+ TCLSH_CMD=none
+ else
+ TCLSH_CMD=${TCLSH_CMD_2}
+ fi
+ fi
+fi
if test "$TCLSH_CMD" = "none"; then
# If we can't find a local tclsh, then building the amalgamation will fail.
# We act as though --disable-amalgamation has been used.
- echo "Warning: can't find tclsh - defaulting to non-amalgamation build."
+ AC_MSG_WARN([Warning: can't find tclsh - defaulting to non-amalgamation build.])
USE_AMALGAMATION=0
TCLSH_CMD="tclsh"
fi
@@ -132,14 +218,27 @@ AC_SUBST(TCLSH_CMD)
AC_ARG_VAR([TCLLIBDIR], [Where to install tcl plugin])
if test "x${TCLLIBDIR+set}" != "xset" ; then
- TCLLIBDIR='$(libdir)'
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
- TCLLIBDIR=$i
- break
+ if test -d $i ; then
+ TCLLIBDIR=$i
+ break
+ fi
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
+#########
+# Set up options for running tests.
+#
+AC_ARG_ENABLE(test-status, AS_HELP_STRING([--enable-test-status],[Full-screen status of tests]),
+ [use_vt100=$enableval],[use_vt100=no])
+if test $use_vt100 != no; then
+ TSTRNNR_OPTS=--status
+else
+ TSTRNNR_OPTS=
+fi
+AC_SUBST(TSTRNNR_OPTS)
+
#########
# Set up an appropriate program prefix
@@ -155,11 +254,56 @@ AC_SUBST(VERSION)
RELEASE=`cat $srcdir/VERSION`
AC_MSG_NOTICE(Release set to $RELEASE)
AC_SUBST(RELEASE)
-VERSION_NUMBER=[`cat $srcdir/VERSION \
- | sed 's/[^0-9]/ /g' \
- | awk '{printf "%d%03d%03d",$1,$2,$3}'`]
-AC_MSG_NOTICE(Version number set to $VERSION_NUMBER)
-AC_SUBST(VERSION_NUMBER)
+
+##########
+# Handle --with-wasi-sdk=DIR
+#
+# This must be early because it changes the toolchain.
+#
+AC_ARG_WITH(wasi-sdk,
+AS_HELP_STRING([--with-wasi-sdk=DIR],
+ [directory containing the WASI SDK. Triggers cross-compile to WASM.]), with_wasisdk=${withval})
+AC_MSG_CHECKING([for WASI SDK directory])
+AC_CACHE_VAL(ac_cv_c_wasi_sdk,[
+ # First check to see if --with-tcl was specified.
+ if test x"${with_wasi_sdk}" != x ; then
+ if ! test -d "${with_wasi_sdk}" ; then
+ AC_MSG_ERROR([${with_wasi_sdk} directory doesn't exist])
+ fi
+ AC_MSG_RESULT([${with_wasi_sdk}: using wasi-sdk clang, disabling: tcl, CLI shell, DLL])
+ use_wasi_sdk=yes
+ else
+ use_wasi_sdk=no
+ fi
+])
+if test "${use_wasi_sdk}" = "no" ; then
+ HAVE_WASI_SDK=""
+ AC_MSG_RESULT([no])
+else
+ HAVE_WASI_SDK=1
+# Changing --host and --target have no effect here except to possibly
+# cause confusion. autoconf has finished processing them by this
+# point.
+#
+# host_alias=wasm32-wasi
+# target=wasm32-wasi
+#
+# Merely changing CC and LD to the wasi-sdk's is enough to get
+# sqlite3.o building in WASM format.
+ CC="${with_wasi_sdk}/bin/clang"
+ LD="${with_wasi_sdk}/bin/wasm-ld"
+ RANLIB="${with_wasi_sdk}/bin/llvm-ranlib"
+ cross_compiling=yes
+ enable_threadsafe=no
+ use_tcl=no
+ enable_tcl=no
+ # libtool is apparently hard-coded to use gcc for linking DLLs, so
+ # we disable the DLL build...
+ enable_shared=no
+ AC_MSG_RESULT([yes])
+fi
+AC_SUBST(HAVE_WASI_SDK)
+
#########
# Locate a compiler for the build machine. This compiler should
@@ -182,7 +326,7 @@ AC_SUBST(BUILD_CC)
# Do we want to support multithreaded use of sqlite
#
AC_ARG_ENABLE(threadsafe,
-AC_HELP_STRING([--disable-threadsafe],[Disable mutexing]),,enable_threadsafe=yes)
+AS_HELP_STRING([--disable-threadsafe],[Disable mutexing]))
AC_MSG_CHECKING([whether to support threadsafe operation])
if test "$enable_threadsafe" = "no"; then
SQLITE_THREADSAFE=0
@@ -202,7 +346,7 @@ fi
# Do we want to support release
#
AC_ARG_ENABLE(releasemode,
-AC_HELP_STRING([--enable-releasemode],[Support libtool link to release mode]),,enable_releasemode=no)
+AS_HELP_STRING([--enable-releasemode],[Support libtool link to release mode]),,enable_releasemode=no)
AC_MSG_CHECKING([whether to support shared library linked as release mode or not])
if test "$enable_releasemode" = "no"; then
ALLOWRELEASE=""
@@ -217,7 +361,7 @@ AC_SUBST(ALLOWRELEASE)
# Do we want temporary databases in memory
#
AC_ARG_ENABLE(tempstore,
-AC_HELP_STRING([--enable-tempstore],[Use an in-ram database for temporary tables (never,no,yes,always)]),,enable_tempstore=no)
+AS_HELP_STRING([--enable-tempstore],[Use an in-ram database for temporary tables (never,no,yes,always)]),,enable_tempstore=no)
AC_MSG_CHECKING([whether to use an in-ram database for temporary tables])
case "$enable_tempstore" in
never )
@@ -257,7 +401,15 @@ else
AC_MSG_RESULT(unknown)
fi
if test "$CYGWIN" != "yes"; then
- AC_CYGWIN
+ m4_warn([obsolete],
+[AC_CYGWIN is obsolete: use AC_CANONICAL_HOST and check if $host_os
+matches *cygwin*])dnl
+AC_CANONICAL_HOST
+case $host_os in
+ *cygwin* ) CYGWIN=yes;;
+ * ) CYGWIN=no;;
+esac
+
fi
if test "$CYGWIN" = "yes"; then
BUILD_EXEEXT=.exe
@@ -284,190 +436,6 @@ AC_SUBST(SQLITE_OS_UNIX)
AC_SUBST(SQLITE_OS_WIN)
AC_SUBST(TARGET_EXEEXT)
-##########
-# Figure out all the parameters needed to compile against Tcl.
-#
-# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
-# macros in the in the tcl.m4 file of the standard TCL distribution.
-# Those macros could not be used directly since we have to make some
-# minor changes to accomodate systems that do not have TCL installed.
-#
-AC_ARG_ENABLE(tcl, AC_HELP_STRING([--disable-tcl],[do not build TCL extension]),
- [use_tcl=$enableval],[use_tcl=yes])
-if test "${use_tcl}" = "yes" ; then
- AC_ARG_WITH(tcl, AC_HELP_STRING([--with-tcl=DIR],[directory containing tcl configuration (tclConfig.sh)]), with_tclconfig=${withval})
- AC_MSG_CHECKING([for Tcl configuration])
- AC_CACHE_VAL(ac_cv_c_tclconfig,[
- # First check to see if --with-tcl was specified.
- if test x"${with_tclconfig}" != x ; then
- if test -f "${with_tclconfig}/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
- else
- AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
- fi
- fi
-
- # Start autosearch by asking tclsh
- if test x"${ac_cv_c_tclconfig}" = x ; then
- if test x"$cross_compiling" = xno; then
- for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}`
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig="$i"
- break
- fi
- done
- fi
- fi
-
- # On ubuntu 14.10, $auto_path on tclsh is not quite correct.
- # So try again after applying corrections.
- if test x"${ac_cv_c_tclconfig}" = x ; then
- if test x"$cross_compiling" = xno; then
- for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD} | sed 's,/tcltk/tcl,/tcl,g'`
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig="$i"
- break
- fi
- done
- fi
- fi
-
- # Recent versions of Xcode on Macs hid the tclConfig.sh file
- # in a strange place.
- if test x"${ac_cv_c_tclconfig}" = x ; then
- if test x"$cross_compiling" = xno; then
- for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig="$i"
- break
- fi
- done
- fi
- fi
-
- # then check for a private Tcl installation
- if test x"${ac_cv_c_tclconfig}" = x ; then
- for i in \
- ../tcl \
- `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
- `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
- `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
- ../../tcl \
- `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
- `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
- `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
- ../../../tcl \
- `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
- `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
- `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null`
- do
- if test -f "$i/unix/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
- break
- fi
- done
- fi
-
- # check in a few common install locations
- if test x"${ac_cv_c_tclconfig}" = x ; then
- for i in \
- `ls -d ${libdir} 2>/dev/null` \
- `ls -d /usr/local/lib 2>/dev/null` \
- `ls -d /usr/contrib/lib 2>/dev/null` \
- `ls -d /usr/lib 2>/dev/null`
- do
- if test -f "$i/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd $i; pwd)`
- break
- fi
- done
- fi
-
- # check in a few other private locations
- if test x"${ac_cv_c_tclconfig}" = x ; then
- for i in \
- ${srcdir}/../tcl \
- `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
- `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
- `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null`
- do
- if test -f "$i/unix/tclConfig.sh" ; then
- ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
- break
- fi
- done
- fi
- ])
-
- if test x"${ac_cv_c_tclconfig}" = x ; then
- use_tcl=no
- AC_MSG_WARN(Can't find Tcl configuration definitions)
- AC_MSG_WARN(*** Without Tcl the regression tests cannot be executed ***)
- AC_MSG_WARN(*** Consider using --with-tcl=... to define location of Tcl ***)
- else
- TCL_BIN_DIR=${ac_cv_c_tclconfig}
- AC_MSG_RESULT(found $TCL_BIN_DIR/tclConfig.sh)
-
- AC_MSG_CHECKING([for existence of $TCL_BIN_DIR/tclConfig.sh])
- if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
- AC_MSG_RESULT([loading])
- . $TCL_BIN_DIR/tclConfig.sh
- else
- AC_MSG_RESULT([file not found])
- fi
-
- #
- # If the TCL_BIN_DIR is the build directory (not the install directory),
- # then set the common variable name to the value of the build variables.
- # For example, the variable TCL_LIB_SPEC will be set to the value
- # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
- # instead of TCL_BUILD_LIB_SPEC since it will work with both an
- # installed and uninstalled version of Tcl.
- #
-
- if test -f $TCL_BIN_DIR/Makefile ; then
- TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
- TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
- TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
- fi
-
- #
- # eval is required to do the TCL_DBGX substitution
- #
-
- eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
- eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
- eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
-
- eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
- eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
- eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
-
- AC_SUBST(TCL_VERSION)
- AC_SUBST(TCL_BIN_DIR)
- AC_SUBST(TCL_SRC_DIR)
- AC_SUBST(TCL_INCLUDE_SPEC)
-
- AC_SUBST(TCL_LIB_FILE)
- AC_SUBST(TCL_LIB_FLAG)
- AC_SUBST(TCL_LIB_SPEC)
-
- AC_SUBST(TCL_STUB_LIB_FILE)
- AC_SUBST(TCL_STUB_LIB_FLAG)
- AC_SUBST(TCL_STUB_LIB_SPEC)
- AC_SUBST(TCL_SHLIB_SUFFIX)
- fi
-fi
-if test "${use_tcl}" = "no" ; then
- HAVE_TCL=""
-else
- HAVE_TCL=1
-fi
-AC_SUBST(HAVE_TCL)
-
##########
# Figure out what C libraries are required to compile programs
# that use "readline()" library.
@@ -477,11 +445,11 @@ TARGET_READLINE_INC=""
TARGET_HAVE_READLINE=0
TARGET_HAVE_EDITLINE=0
AC_ARG_ENABLE([editline],
- [AC_HELP_STRING([--enable-editline],[enable BSD editline support])],
+ [AS_HELP_STRING([--enable-editline],[enable BSD editline support])],
[with_editline=$enableval],
[with_editline=auto])
AC_ARG_ENABLE([readline],
- [AC_HELP_STRING([--disable-readline],[disable readline support])],
+ [AS_HELP_STRING([--disable-readline],[disable readline support])],
[with_readline=$enableval],
[with_readline=auto])
@@ -497,7 +465,7 @@ if test x"$with_readline" != xno; then
found="yes"
AC_ARG_WITH([readline-lib],
- [AC_HELP_STRING([--with-readline-lib],[specify readline library])],
+ [AS_HELP_STRING([--with-readline-lib],[specify readline library])],
[with_readline_lib=$withval],
[with_readline_lib="auto"])
if test "x$with_readline_lib" = xauto; then
@@ -512,7 +480,7 @@ if test x"$with_readline" != xno; then
fi
AC_ARG_WITH([readline-inc],
- [AC_HELP_STRING([--with-readline-inc],[specify readline include paths])],
+ [AS_HELP_STRING([--with-readline-inc],[specify readline include paths])],
[with_readline_inc=$withval],
[with_readline_inc="auto"])
if test "x$with_readline_inc" = xauto; then
@@ -543,11 +511,28 @@ if test x"$with_readline" != xno; then
TARGET_HAVE_READLINE=1
fi
fi
+AC_ARG_WITH([linenoise],
+ [AS_HELP_STRING([--with-linenoise=DIR],[source directory for linenoise library])],
+ [with_linenoise=$withval],
+ [with_linenoise="no"])
+if test "x$with_linenoise" != "xno"; then
+ TARGET_HAVE_READLINE=0
+ TARGET_HAVE_EDITLINE=0
+ TARGET_HAVE_LINENOISE=1
+ TARGET_READLINE_INC="-I${with_linenoise}"
+ TARGET_READLINE_LIBS="${with_linenoise}/linenoise.c"
+ echo "using linenoise source code at ${with_linenoise}"
+else
+ TARGET_HAVE_LINENOISE=0
+ echo "not using linenoise"
+fi
AC_SUBST(TARGET_READLINE_LIBS)
AC_SUBST(TARGET_READLINE_INC)
AC_SUBST(TARGET_HAVE_READLINE)
AC_SUBST(TARGET_HAVE_EDITLINE)
+AC_SUBST(TARGET_HAVE_LINENOISE)
+
##########
# Figure out what C libraries are required to compile programs
@@ -557,56 +542,94 @@ AC_SEARCH_LIBS(fdatasync, [rt])
#########
# check for debug enabled
-AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]),
- [use_debug=$enableval],[use_debug=no])
-if test "${use_debug}" = "yes" ; then
- TARGET_DEBUG="-DSQLITE_DEBUG=1"
+AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[enable debugging & verbose explain]))
+AC_MSG_CHECKING([build type])
+if test "${enable_debug}" = "yes" ; then
+ TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
+ AC_MSG_RESULT([debug])
else
TARGET_DEBUG="-DNDEBUG"
+ AC_MSG_RESULT([release])
fi
AC_SUBST(TARGET_DEBUG)
#########
# See whether we should use the amalgamation to build
-AC_ARG_ENABLE(amalgamation, AC_HELP_STRING([--disable-amalgamation],
- [Disable the amalgamation and instead build all files separately]),
- [use_amalgamation=$enableval],[use_amalgamation=yes])
-if test "${use_amalgamation}" != "yes" ; then
+
+AC_ARG_ENABLE(amalgamation, AS_HELP_STRING([--disable-amalgamation],
+ [Disable the amalgamation and instead build all files separately]))
+if test "${enable_amalgamation}" = "no" ; then
USE_AMALGAMATION=0
fi
AC_SUBST(USE_AMALGAMATION)
+#########
+# Look for zlib. Only needed by extensions and by the sqlite3.exe shell
+AC_CHECK_HEADERS(zlib.h)
+AC_SEARCH_LIBS(deflate, z, [HAVE_ZLIB="-DSQLITE_HAVE_ZLIB=1"], [HAVE_ZLIB=""])
+AC_SUBST(HAVE_ZLIB)
+
#########
# See whether we should allow loadable extensions
-AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--disable-load-extension],
- [Disable loading of external extensions]),
- [use_loadextension=$enableval],[use_loadextension=yes])
-if test "${use_loadextension}" = "yes" ; then
+AC_ARG_ENABLE(load-extension, AS_HELP_STRING([--disable-load-extension],
+ [Disable loading of external extensions]),,[enable_load_extension=yes])
+if test "${enable_load_extension}" = "yes" ; then
OPT_FEATURE_FLAGS=""
AC_SEARCH_LIBS(dlopen, dl)
else
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
+##########
+# Do we want to support math functions
+#
+AC_ARG_ENABLE(math,
+AS_HELP_STRING([--disable-math],[Disable math functions]))
+AC_MSG_CHECKING([whether to support math functions])
+if test "$enable_math" = "no"; then
+ AC_MSG_RESULT([no])
+else
+ AC_MSG_RESULT([yes])
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MATH_FUNCTIONS"
+ AC_SEARCH_LIBS(ceil, m)
+fi
+
+##########
+# Do we want to support JSON functions
+#
+AC_ARG_ENABLE(json,
+AS_HELP_STRING([--disable-json],[Disable JSON functions]))
+AC_MSG_CHECKING([whether to support JSON functions])
+if test "$enable_json" = "no"; then
+ AC_MSG_RESULT([no])
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_OMIT_JSON"
+else
+ AC_MSG_RESULT([yes])
+fi
+
+########
+# The --enable-all argument is short-hand to enable
+# multiple extensions.
+AC_ARG_ENABLE(all, AS_HELP_STRING([--enable-all],
+ [Enable FTS4, FTS5, Geopoly, RTree, Sessions]))
+
##########
# Do we want to support memsys3 and/or memsys5
#
AC_ARG_ENABLE(memsys5,
- AC_HELP_STRING([--enable-memsys5],[Enable MEMSYS5]),
- [enable_memsys5=yes],[enable_memsys5=no])
+ AS_HELP_STRING([--enable-memsys5],[Enable MEMSYS5]))
AC_MSG_CHECKING([whether to support MEMSYS5])
if test "${enable_memsys5}" = "yes"; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS5"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS5"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
AC_ARG_ENABLE(memsys3,
- AC_HELP_STRING([--enable-memsys3],[Enable MEMSYS3]),
- [enable_memsys3=yes],[enable_memsys3=no])
+ AS_HELP_STRING([--enable-memsys3],[Enable MEMSYS3]))
AC_MSG_CHECKING([whether to support MEMSYS3])
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS3"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS3"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
@@ -614,57 +637,90 @@ fi
#########
# See whether we should enable Full Text Search extensions
-AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3],
- [Enable the FTS3 extension]),
- [enable_fts3=yes],[enable_fts3=no])
+AC_ARG_ENABLE(fts3, AS_HELP_STRING([--enable-fts3],
+ [Enable the FTS3 extension]))
+AC_MSG_CHECKING([whether to support FTS3])
if test "${enable_fts3}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS3"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3"
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
fi
-AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4],
- [Enable the FTS4 extension]),
- [enable_fts4=yes],[enable_fts4=no])
-if test "${enable_fts4}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS4"
+AC_ARG_ENABLE(fts4, AS_HELP_STRING([--enable-fts4],
+ [Enable the FTS4 extension]))
+AC_MSG_CHECKING([whether to support FTS4])
+if test "${enable_fts4}" = "yes" -o "${enable_all}" = "yes" ; then
+ AC_MSG_RESULT([yes])
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
AC_SEARCH_LIBS([log],[m])
+else
+ AC_MSG_RESULT([no])
fi
-AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5],
- [Enable the FTS5 extension]),
- [enable_fts5=yes],[enable_fts5=no])
-if test "${enable_fts5}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS5"
+AC_ARG_ENABLE(fts5, AS_HELP_STRING([--enable-fts5],
+ [Enable the FTS5 extension]))
+AC_MSG_CHECKING([whether to support FTS5])
+if test "${enable_fts5}" = "yes" -o "${enable_all}" = "yes" ; then
+ AC_MSG_RESULT([yes])
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
AC_SEARCH_LIBS([log],[m])
+else
+ AC_MSG_RESULT([no])
fi
#########
-# See whether we should enable JSON1
-AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1],
- [Enable the JSON1 extension]),
- [enable_json1=yes],[enable_json1=no])
-if test "${enable_json1}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_JSON1"
+# See whether we should enable the LIMIT clause on UPDATE and DELETE
+# statements.
+AC_ARG_ENABLE(update-limit, AS_HELP_STRING([--enable-update-limit],
+ [Enable the UPDATE/DELETE LIMIT clause]))
+AC_MSG_CHECKING([whether to support LIMIT on UPDATE and DELETE statements])
+if test "${enable_update_limit}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+#########
+# See whether we should enable GEOPOLY
+AC_ARG_ENABLE(geopoly, AS_HELP_STRING([--enable-geopoly],
+ [Enable the GEOPOLY extension]),
+ [enable_geopoly=yes],[enable_geopoly=no])
+AC_MSG_CHECKING([whether to support GEOPOLY])
+if test "${enable_geopoly}" = "yes" -o "${enable_all}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
+ enable_rtree=yes
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
fi
#########
# See whether we should enable RTREE
-AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree],
- [Enable the RTREE extension]),
- [enable_rtree=yes],[enable_rtree=no])
+AC_ARG_ENABLE(rtree, AS_HELP_STRING([--enable-rtree],
+ [Enable the RTREE extension]))
+AC_MSG_CHECKING([whether to support RTREE])
if test "${enable_rtree}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_RTREE"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE"
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
fi
#########
# See whether we should enable the SESSION extension
-AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session],
- [Enable the SESSION extension]),
- [enable_session=yes],[enable_session=no])
-if test "${enable_session}" = "yes" ; then
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_SESSION"
- OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_PREUPDATE_HOOK"
+AC_ARG_ENABLE(session, AS_HELP_STRING([--enable-session],
+ [Enable the SESSION extension]))
+AC_MSG_CHECKING([whether to support SESSION])
+if test "${enable_session}" = "yes" -o "${enable_all}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
fi
#########
-# attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter
+# attempt to duplicate any OMITS and ENABLES into the ${OPT_FEATURE_FLAGS} parameter
for option in $CFLAGS $CPPFLAGS
do
case $option in
@@ -716,9 +772,8 @@ BUILD_CFLAGS=$ac_temp_BUILD_CFLAGS
#########
# See whether we should use GCOV
-AC_ARG_ENABLE(gcov, AC_HELP_STRING([--enable-gcov],
- [Enable coverage testing using gcov]),
- [use_gcov=$enableval],[use_gcov=no])
+AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov],
+ [Enable coverage testing using gcov]))
if test "${use_gcov}" = "yes" ; then
USE_GCOV=1
else
@@ -726,16 +781,28 @@ else
fi
AC_SUBST(USE_GCOV)
+#########
+# Enable/disabled amalagamation line macros
+########
+AMALGAMATION_LINE_MACROS=--linemacros=0
+if test "${amalgamation_line_macros}" = "yes" ; then
+ AMALGAMATION_LINE_MACROS=--linemacros=1
+fi
+if test "${amalgamation_line_macros}" = "no" ; then
+ AMALGAMATION_LINE_MACROS=--linemacros=0
+fi
+AC_SUBST(AMALGAMATION_LINE_MACROS)
#########
# Output the config header
-AC_CONFIG_HEADERS(config.h)
+AC_CONFIG_HEADERS(sqlite_cfg.h)
#########
# Generate the output files.
#
AC_SUBST(BUILD_CFLAGS)
-AC_OUTPUT([
+AC_CONFIG_FILES([
Makefile
sqlite3.pc
])
+AC_OUTPUT
diff --git a/sqlite/doc/F2FS.txt b/sqlite/doc/F2FS.txt
new file mode 100644
index 00000000..47ad2297
--- /dev/null
+++ b/sqlite/doc/F2FS.txt
@@ -0,0 +1,87 @@
+
+SQLite's OS layer contains the following definitions used in F2FS related
+calls:
+
+#define F2FS_IOCTL_MAGIC 0xf5
+#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1)
+#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2)
+#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
+#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
+#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32)
+#define F2FS_FEATURE_ATOMIC_WRITE 0x0004
+
+After opening a database file on Linux (including Android), SQLite determines
+whether or not a file supports F2FS atomic commits as follows:
+
+ u32 flags = 0;
+ rc = ioctl(fd, F2FS_IOC_GET_FEATURES, &flags);
+ if( rc==0 && (flags & F2FS_FEATURE_ATOMIC_WRITE) ){
+ /* File supports F2FS atomic commits */
+ }else{
+ /* File does NOT support F2FS atomic commits */
+ }
+
+where "fd" is the file-descriptor open on the database file.
+
+Usually, when writing to a database file that supports atomic commits, SQLite
+accumulates the entire transaction in heap memory, deferring all writes to the
+db file until the transaction is committed.
+
+When it is time to commit a transaction on a file that supports atomic
+commits, SQLite does:
+
+ /* Take an F_WRLCK lock on the database file. This prevents any other
+ ** SQLite clients from reading or writing the file until the lock
+ ** is released. */
+ rc = fcntl(fd, F_SETLK, ...);
+ if( rc!=0 ) goto failed;
+
+ rc = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE);
+ if( rc!=0 ) goto fallback_to_legacy_journal_commit;
+
+ foreach (dirty page){
+ rc = write(fd, ...dirty page...);
+ if( rc!=0 ){
+ ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE);
+ goto fallback_to_legacy_journal_commit;
+ }
+ }
+
+ rc = ioctl(fd, F2FS_IOC_COMMIT_ATOMIC_WRITE);
+ if( rc!=0 ){
+ ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE);
+ goto fallback_to_legacy_journal_commit;
+ }
+
+ /* If we get there, the transaction has been successfully
+ ** committed to persistent storage. The following call
+ ** relinquishes the F_WRLCK lock. */
+ fcntl(fd, F_SETLK, ...);
+
+Assumptions:
+
+1. After either of the F2FS_IOC_ABORT_VOLATILE_WRITE calls return,
+ the database file is in the state that it was in before
+ F2FS_IOC_START_ATOMIC_WRITE was invoked. Even if the ioctl()
+ fails - we're ignoring the return code.
+
+ This is true regardless of the type of error that occurred in
+ ioctl() or write().
+
+2. If the system fails before the F2FS_IOC_COMMIT_ATOMIC_WRITE is
+ completed, then following a reboot the database file is in the
+ state that it was in before F2FS_IOC_START_ATOMIC_WRITE was invoked.
+ Or, if the write was commited right before the system failed, in a
+ state indicating that all write() calls were successfully committed
+ to persistent storage before the failure occurred.
+
+3. If the process crashes before the F2FS_IOC_COMMIT_ATOMIC_WRITE is
+ completed then the file is automatically restored to the state that
+ it was in before F2FS_IOC_START_ATOMIC_WRITE was called. This occurs
+ before the posix advisory lock is automatically dropped - there is
+ no chance that another client will be able to read the file in a
+ half-committed state before the rollback operation occurs.
+
+
+
+
diff --git a/sqlite/doc/compile-for-unix.md b/sqlite/doc/compile-for-unix.md
new file mode 100644
index 00000000..a5caa8b1
--- /dev/null
+++ b/sqlite/doc/compile-for-unix.md
@@ -0,0 +1,57 @@
+# Notes On Compiling SQLite On All Kinds Of Unix
+
+Here are step-by-step instructions on how to build SQLite from
+canonical source on any modern machine that isn't Windows. These
+notes are tested (on 2024-10-11) on Ubuntu and on MacOS, but they
+are general and should work on most any modern unix platform.
+
+ 1. Install a C-compiler. GCC or Clang both work fine. If you are
+ reading this document, you've probably already done that.
+
+ 2. Install TCL9 development libraries. In this note, we'll do a
+ private install in the $HOME/local directory, but you can make
+ adjustments to install TCL9 wherever you like.
+
+ This document assumes you are working with TCL version 9.0.
+
+
Get the TCL source archive, perhaps from
+
+ or .
+
Untar the source archive. CD into the "unix/" subfolder
+ of the source tree.
+
Run: `mkdir $HOME/local`
+
Run: `./configure --prefix=$HOME/local`
+
Run: `make install`
+
+
+ 4. Download the SQLite source tree and unpack it. CD into the
+ toplevel directory of the source tree.
+
+ 5. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
+
+ You do not need to use --with-tclsh if the tclsh you want to use is the
+ first one on your PATH.
+
+ 6. Run the "`Makefile`" makefile with an appropriate target.
+ Examples:
+
+
`make sqlite3.c`
+
`make sqlite3`
+
`make sqldiff`
+
`make sqlite3_rsync`
+
`make tclextension-install`
+
`make devtest`
+
`make releasetest`
+
`make sqlite3_analyzer`
+
+
+ It is not required that you run the "tclextension-install" target prior to
+ running tests. However, the tests will run more smoothly if you do.
+ The version of SQLite used for the TCL extension does *not* need to
+ correspond to the version of SQLite under test. So you can install the
+ SQLite TCL extension once, and then use it to test many different versions
+ of SQLite.
+
+
+ 7. For a debugging build of the CLI, where the ".treetrace" and ".wheretrace"
+ commands work, add the the --enable-debug argument to configure.
diff --git a/sqlite/doc/compile-for-windows.md b/sqlite/doc/compile-for-windows.md
new file mode 100644
index 00000000..05893b56
--- /dev/null
+++ b/sqlite/doc/compile-for-windows.md
@@ -0,0 +1,173 @@
+# Notes On Compiling SQLite On Windows 11
+
+Here are step-by-step instructions on how to build SQLite from
+canonical source on a new Windows 11 PC, as of 2024-10-09:
+
+ 1. Install Microsoft Visual Studio. The free "community edition"
+ will work fine. Do a standard install for C++ development.
+ SQLite only needs the
+ "cl" compiler and the "nmake" build tool.
+
+ 2. Under the "Start" menu, find "All Apps" then go to "Visual Studio 20XX"
+ and find "x64 Native Tools Command Prompt for VS 20XX". Pin that
+ application to your task bar, as you will use it a lot. Bring up
+ an instance of this command prompt and do all of the subsequent steps
+ in that "x64 Native Tools" command prompt. (Or use "x86" if you want
+ a 32-bit build.) The subsequent steps will not work in a vanilla
+ DOS prompt. Nor will they work in PowerShell.
+
+ 3. Install TCL development libraries. This note assumes that you will
+ install the TCL development libraries in the "`c:\Tcl`" directory.
+ Make adjustments
+ if you want TCL installed somewhere else. SQLite needs both the
+ "tclsh90.exe" command-line tool as part of the build process, and
+ the "tcl90.lib" and "tclstub.lib" libraries in order to run tests.
+ This document assumes you are working with TCL version 9.0.
+ See versions of this document from prior to 2024-10-10 for
+ instructions on how to build using TCL version 8.6.
+
+
Get the TCL source archive, perhaps from
+
+ or .
+
Untar or unzip the source archive. CD into the "win/" subfolder
+ of the source tree.
+
Optional: CD to `c:\Tcl\bin` and make a copy of
+ `tclsh90.exe` over into just `tclsh.exe`.
+
Optional:
+ Add `c:\Tcl\bin` to your %PATH%. To do this, go to Settings
+ and search for "path". Select "edit environment variables for
+ your account" and modify your default PATH accordingly.
+ You will need to close and reopen your command prompts after
+ making this change.
+
+
+ 4. Download the SQLite source tree and unpack it. CD into the
+ toplevel directory of the source tree.
+
+ 5. Set the TCLDIR environment variable to point to your TCL installation.
+ Like this:
+
+
`set TCLDIR=c:\Tcl`
+
+
+ If you install TCL in the "`c:\Tcl`" directory (as recommended
+ in step 3 above), then this step is optional because
+ "`c:\Tcl`" is the default value for TCLDIR. You can also skip this
+ step by specifying "`TCLDIR=c:\Tcl`" as an argument to the nmake
+ commands in step 6 below.
+
+ 6. Run the "`Makefile.msc`" makefile with an appropriate target.
+ Examples:
+
+
`nmake /f makefile.msc`
+
`nmake /f makefile.msc sqlite3.c`
+
`nmake /f makefile.msc sqlite3.exe`
+
`nmake /f makefile.msc sqldiff.exe`
+
`nmake /f makefile.msc sqlite3_rsync.exe`
+
`nmake /f makefile.msc tclextension-install`
+
`nmake /f makefile.msc devtest`
+
`nmake /f makefile.msc releasetest`
+
`nmake /f makefile.msc sqlite3_analyzer.exe`
+
+
+ It is not required that you run the "tclextension-install" target prior to
+ running tests. However, the tests will run more smoothly if you do.
+ The version of SQLite used for the TCL extension does *not* need to
+ correspond to the version of SQLite under test. So you can install the
+ SQLite TCL extension once, and then use it to test many different versions
+ of SQLite.
+
+
+ 7. For a debugging build of the CLI, where the ".treetrace" and ".wheretrace"
+ commands work, add the DEBUG=3 argument to nmake. Like this:
+
+
+
+## 32-bit Builds
+
+Doing a 32-bit build is just like doing a 64-bit build with the
+following minor changes:
+
+ 1. Use the "x86 Native Tools Command Prompt" instead of
+ "x64 Native Tools Command Prompt". "**x86**" instead of "**x64**".
+
+ 2. Use a different installation directory for TCL.
+ The recommended directory is `c:\tcl32`. Thus you end up
+ with two TCL builds:
+
+
`c:\tcl` ← 64-bit (the default)
+
`c:\tcl32` ← 32-bit
+
+
+ 3. Ensure that `c:\tcl32\bin` comes before `c:\tcl\bin` on
+ your PATH environment variable. You can achieve this using
+ a command like:
+
+
`set PATH=c:\tcl32\bin;%PATH%`
+
+
+## Building a DLL
+
+The command the developers use for building the deliverable DLL on the
+[download page](https://sqlite.org/download.html) is as follows:
+
+> ~~~~
+nmake /f Makefile.msc sqlite3.dll USE_NATIVE_LIBPATHS=1 "OPTS=-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_GEOPOLY=1 -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 -DSQLITE_ENABLE_SERIALIZE=1 -DSQLITE_ENABLE_MATH_FUNCTIONS=1"
+~~~~
+
+That command generates both the sqlite3.dll and sqlite3.def files. The same
+command works for both 32-bit and 64-bit builds.
+
+## Statically Linking The TCL Library
+
+Some utility programs associated with SQLite need to be linked
+with TCL in order to function. The [sqlite3_analyzer.exe program](https://sqlite.org/sqlanalyze.html)
+is an example. You can build as described above, and then
+enter:
+
+> ~~~~
+nmake /f Makefile.msc sqlite3_analyzer.exe
+~~~~
+
+And you will end up with a working executable. However, that executable
+will depend on having the "tcl98.dll" library somewhere on your %PATH%.
+Use the following steps to build an executable that has the TCL library
+statically linked so that it does not depend on separate DLL:
+
+ 1. Use the appropriate "Command Prompt" window - either x86 or
+ x64, depending on whether you want a 32-bit or 64-bit executable.
+
+ 2. Untar the TCL source tarball into a fresh directory. CD into
+ the "win/" subfolder.
+
+ 3. Run: `nmake /f makefile.vc OPTS=static shell`
+
+ 4. CD into the "Release*" subfolder that is created (note the
+ wildcard - the full name of the directory might vary). There
+ you will find the "tcl90s.lib" file. Copy this file into the
+ same directory that you put the "tcl90.lib" on your initial
+ installation. (In this document, that directory is
+ "`C:\Tcl32\lib`" for 32-bit builds and
+ "`C:\Tcl\lib`" for 64-bit builds.)
+
+ 5. CD into your SQLite source code directory and build the desired
+ utility program, but add the following extra argument to the
+ nmake command line:
+
+ STATICALLY_LINK_TCL=1
+
+
So, for example, to build a statically linked version of
+ sqlite3_analyzer.exe, you might type:
+
+
+ 6. After your executable is built, you can verify that it does not
+ depend on the TCL DLL by running:
+
+ dumpbin /dependents sqlite3_analyzer.exe
+
diff --git a/sqlite/doc/json-enhancements.md b/sqlite/doc/json-enhancements.md
new file mode 100644
index 00000000..bc03e897
--- /dev/null
+++ b/sqlite/doc/json-enhancements.md
@@ -0,0 +1,144 @@
+# JSON Functions Enhancements (2022)
+
+This document summaries enhancements to the SQLite JSON support added in
+early 2022.
+
+## 1.0 Change summary:
+
+ 1. New **->** and **->>** operators that work like MySQL and PostgreSQL (PG).
+ 2. JSON functions are built-in rather than being an extension. They
+ are included by default, but can be omitted using the
+ -DSQLITE_OMIT_JSON compile-time option.
+
+
+## 2.0 New operators **->** and **->>**
+
+The SQLite language adds two new binary operators **->** and **->>**.
+Both operators are similar to json_extract(). The left operand is
+JSON and the right operand is a JSON path expression (possibly abbreviated
+for compatibility with PG - see below). So they are similar to a
+two-argument call to json_extract().
+
+The difference between -> and ->> (and json_extract()) is as follows:
+
+ * The -> operator always returns JSON.
+
+ * The ->> operator converts the answer into a primitive SQL datatype
+ such as TEXT, INTEGER, REAL, or NULL. If a JSON object or array
+ is selected, that object or array is rendered as text. If a JSON
+ value is selected, that value is converted into its corresponding
+ SQL type
+
+ * The json_extract() interface returns JSON when a JSON object or
+ array is selected, or a primitive SQL datatype when a JSON value
+ is selected. This is different from MySQL, in which json_extract()
+ always returns JSON, but the difference is retained because it has
+ worked that way for 6 years and changing it now would likely break
+ a lot of legacy code.
+
+In MySQL and PG, the ->> operator always returns TEXT (or NULL) and never
+INTEGER or REAL. This is due to limitations in the type handling capabilities
+of those systems. In MySQL and PG, the result type a function or operator
+may only depend on the type of its arguments, never the value of its arguments.
+But the underlying JSON type depends on the value of the JSON path
+expression, not the type of the JSON path expression (which is always TEXT).
+Hence, the result type of ->> in MySQL and PG is unable to vary according
+to the type of the JSON value being extracted.
+
+The type system in SQLite is more general. Functions in SQLite are able
+to return different datatypes depending on the value of their arguments.
+So the ->> operator in SQLite is able to return TEXT, INTEGER, REAL, or NULL
+depending on the JSON type of the value being extracted. This means that
+the behavior of the ->> is slightly different in SQLite versus MySQL and PG
+in that it will sometimes return INTEGER and REAL values, depending on its
+inputs. It is possible to implement the ->> operator in SQLite so that it
+always operates exactly like MySQL and PG and always returns TEXT or NULL,
+but I have been unable to think of any situations where returning the
+actual JSON value this would cause problems, so I'm including the enhanced
+functionality in SQLite.
+
+The table below attempts to summarize the differences between the
+-> and ->> operators and the json_extract() function, for SQLite, MySQL,
+and PG. JSON values are shown using their SQL text representation but
+in a bold font.
+
+
+
+
JSON
PATH
-> operator (all)
->> operator (MySQL/PG)
+
->> operator (SQLite)
json_extract() (SQLite)
+
**'{"a":123}'**
'$.a'
**'123'**
'123'
123
123
+
**'{"a":4.5}'**
'$.a'
**'4.5'**
'4.5'
4.5
4.5
+
**'{"a":"xyz"}'**
'$.a'
**'"xyz"'**
'xyz'
'xyz'
'xyz'
+
**'{"a":null}'**
'$.a'
**'null'**
NULL
NULL
NULL
+
**'{"a":[6,7,8]}'**
'$.a'
**'[6,7,8]'**
'[6,7,8]'
'[6,7,8]'
**'[6,7,8]'**
+
**'{"a":{"x":9}}'**
'$.a'
**'{"x":9}'**
'{"x":9}'
'{"x":9}'
**'{"x":9}'**
+
**'{"b":999}'**
'$.a'
NULL
NULL
NULL
NULL
+
+
+Important points about the table above:
+
+ * The -> operator always returns either JSON or NULL.
+
+ * The ->> operator never returns JSON. It always returns TEXT or NULL, or in the
+ case of SQLite, INTEGER or REAL.
+
+ * The MySQL json_extract() function works exactly the same
+ as the MySQL -> operator.
+
+ * The SQLite json_extract() operator works like -> for JSON objects and
+ arrays, and like ->> for JSON values.
+
+ * The -> operator works the same for all systems.
+
+ * The only difference in ->> between SQLite and other systems is that
+ when the JSON value is numeric, SQLite returns a numeric SQL value,
+ whereas the other systems return a text representation of the numeric
+ value.
+
+### 2.1 Abbreviated JSON path expressions for PG compatibility
+
+The table above always shows the full JSON path expression: '$.a'. But
+PG does not accept this syntax. PG only allows a single JSON object label
+name or a single integer array index. In order to provide compatibility
+with PG, The -> and ->> operators in SQLite are extended to also support
+a JSON object label or an integer array index for the right-hand side
+operand, in addition to a full JSON path expression.
+
+Thus, a -> or ->> operator that works on MySQL will work in
+SQLite. And a -> or ->> operator that works in PG will work in SQLite.
+But because SQLite supports the union of the disjoint capabilities of
+MySQL and PG, there will always be -> and ->> operators that work in
+SQLite that do not work in one of MySQL and PG. This is an unavoidable
+consequence of the different syntax for -> and ->> in MySQL and PG.
+
+In the following table, assume that "value1" is a JSON object and
+"value2" is a JSON array.
+
+
+
SQL expression
Works in MySQL?
Works in PG?
Works in SQLite
+
value1->'$.a'
yes
no
yes
+
value1->'a'
no
yes
yes
+
value2->'$[2]'
yes
no
yes
+
value2->2
no
yes
yes
+
+
+The abbreviated JSON path expressions only work for the -> and ->> operators
+in SQLite. The json_extract() function, and all other built-in SQLite
+JSON functions, continue to require complete JSON path expressions for their
+PATH arguments.
+
+## 3.0 JSON moved into the core
+
+The JSON interface is now moved into the SQLite core.
+
+When originally written in 2015, the JSON functions were an extension
+that could be optionally included at compile-time, or loaded at run-time.
+The implementation was in a source file named ext/misc/json1.c in the
+source tree. JSON functions were only compiled in if the
+-DSQLITE_ENABLE_JSON1 compile-time option was used.
+
+After these enhancements, the JSON functions are now built-ins.
+The source file that implements the JSON functions is moved to src/json.c.
+No special compile-time options are needed to load JSON into the build.
+Instead, there is a new -DSQLITE_OMIT_JSON compile-time option to leave
+them out.
diff --git a/sqlite/doc/jsonb.md b/sqlite/doc/jsonb.md
new file mode 100644
index 00000000..5beed163
--- /dev/null
+++ b/sqlite/doc/jsonb.md
@@ -0,0 +1,290 @@
+# The JSONB Format
+
+This document describes SQLite's JSONB binary encoding of
+JSON.
+
+## 1.0 What Is JSONB?
+
+Beginning with version 3.45.0 (circa 2024-01-01), SQLite supports an
+alternative binary encoding of JSON which we call "JSONB". JSONB is
+a binary format that stored as a BLOB.
+
+The advantage of JSONB over ordinary text RFC 8259 JSON is that JSONB
+is both slightly smaller (by between 5% and 10% in most cases) and
+can be processed in less than half the number of CPU cycles. The built-in
+[JSON SQL functions] of SQLite can accept either ordinary text JSON
+or the binary JSONB encoding for any of their JSON inputs.
+
+The "JSONB" name is inspired by [PostgreSQL](https://postgresql.org), but the
+on-disk format for SQLite's JSONB is not the same as PostgreSQL's.
+The two formats have the same name, but they have wildly different internal
+representations and are not in any way binary compatible.
+
+The central idea behind this JSONB specification is that each element
+begins with a header that includes the size and type of that element.
+The header takes the place of punctuation such as double-quotes,
+curly-brackes, square-brackets, commas, and colons. Since the size
+and type of each element is contained in its header, the element can
+be read faster since it is no longer necessary to carefully scan forward
+looking for the closing delimiter. The payload of JSONB is the same
+as for corresponding text JSON. The same payload bytes occur in the
+same order. The only real difference between JSONB and ordinary text
+JSON is that JSONB includes a binary header on
+each element and omits delimiter and separator punctuation.
+
+### 1.1 Internal Use Only
+
+The details of the JSONB are not intended to be visible to application
+developers. Application developers should look at JSONB as an opaque BLOB
+used internally by SQLite. Nevertheless, we want the format to be backwards
+compatible across all future versions of SQLite. To that end, the format
+is documented by this file in the source tree. But this file should be
+used only by SQLite core developers, not by developers of applications
+that only use SQLite.
+
+## 2.0 The Purpose Of This Document
+
+JSONB is not intended as an external format to be used by
+applications. JSONB is designed for internal use by SQLite only.
+Programmers do not need to understand the JSONB format in order to
+use it effectively.
+Applications should access JSONB only through the [JSON SQL functions],
+not by looking at individual bytes of the BLOB.
+
+However, JSONB is intended to be portable and backwards compatible
+for all future versions of SQLite. In other words, you should not have
+to export and reimport your SQLite database files when you upgrade to
+a newer SQLite version. For that reason, the JSONB format needs to
+be well-defined.
+
+This document is therefore similar in purpose to the
+[SQLite database file format] document that describes the on-disk
+format of an SQLite database file. Applications are not expected
+to directly read and write the bits and bytes of SQLite database files.
+The SQLite database file format is carefully documented so that it
+can be stable and enduring. In the same way, the JSONB representation
+of JSON is documented here so that it too can be stable and enduring,
+not so that applications can read or writes individual bytes.
+
+## 3.0 Encoding
+
+JSONB is a direct translation of the underlying text JSON. The difference
+is that JSONB uses a binary encoding that is faster to parse compared to
+the detailed syntax of text JSON.
+
+Each JSON element is encoded as a header and a payload. The header
+determines type of element (string, numeric, boolean, null, object, or
+array) and the size of the payload. The header can be between 1 and
+9 bytes in size. The payload can be any size from zero bytes up to the
+maximum allowed BLOB size.
+
+### 3.1 Payload Size
+
+The upper four bits of the first byte of the header determine size of the
+header and possibly also the size of the payload.
+If the upper four bits have a value between 0 and 11, then the header is
+exactly one byte in size and the payload size is determined by those
+upper four bits. If the upper four bits have a value between 12 and 15,
+that means that the total header size is 2, 3, 5, or 9 bytes and the
+payload size is unsigned big-endian integer that is contained in the
+subsequent bytes. The size integer is the one byte that following the
+initial header byte if the upper four bits
+are 12, two bytes if the upper bits are 13, four bytes if the upper bits
+are 14, and eight bytes if the upper bits are 15. The current design
+of SQLite does not support BLOB values larger than 2GiB, so the eight-byte
+variant of the payload size integer will never be used by the current code.
+The eight-byte payload size integer is included in the specification
+to allow for future expansion.
+
+The header for an element does *not* need to be in its simplest
+form. For example, consider the JSON numeric value "`1`".
+That element can be encode in five different ways:
+
+ * `0x13 0x31`
+ * `0xc3 0x01 0x31`
+ * `0xd3 0x00 0x01 0x31`
+ * `0xe3 0x00 0x00 0x00 0x01 0x31`
+ * `0xf3 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x31`
+
+The shortest encoding is preferred, of course, and usually happens with
+primitive elements such as numbers. However the total size of an array
+or object might not be known exactly when the header of the element is
+first generated. It is convenient to reserve space for the largest
+possible header and then go back and fill in the correct payload size
+at the end. This technique can result in array or object headers that
+are larger than absolutely necessary.
+
+### 3.2 Element Type
+
+The least-significant four bits of the first byte of the header (the first
+byte masked against 0x0f) determine element type. The following codes are
+used:
+
+
+
NULL →
+The element is a JSON "null". The payload size for a true JSON NULL must
+must be zero. Future versions of SQLite might extend the JSONB format
+with elements that have a zero element type but a non-zero size. In that
+way, legacy versions of SQLite will interpret the element as a NULL
+for backwards compatibility while newer versions will interpret the
+element in some other way.
+
+
TRUE →
+The element is a JSON "true". The payload size must be zero for a actual
+"true" value. Elements with type 1 and a non-zero payload size are
+reserved for future expansion. Legacy implementations that see an element
+type of 1 with a non-zero payload size should continue to interpret that
+element as "true" for compatibility.
+
+
FALSE →
+The element is a JSON "false". The payload size must be zero for a actual
+"false" value. Elements with type 2 and a non-zero payload size are
+reserved for future expansion. Legacy implementations that see an element
+type of 2 with a non-zero payload size should continue to interpret that
+element as "false" for compatibility.
+
+
INT →
+The element is a JSON integer value in the canonical
+RFC 8259 format, without extensions. The payload is the ASCII
+text representation of that numeric value.
+
+
INT5 →
+The element is a JSON integer value that is not in the
+canonical format. The payload is the ASCII
+text representation of that numeric value. Because the payload is in a
+non-standard format, it will need to be translated when the JSONB is
+converted into RFC 8259 text JSON.
+
+
FLOAT →
+The element is a JSON floating-point value in the canonical
+RFC 8259 format, without extensions. The payload is the ASCII
+text representation of that numeric value.
+
+
FLOAT5 →
+The element is a JSON floating-point value that is not in the
+canonical format. The payload is the ASCII
+text representation of that numeric value. Because the payload is in a
+non-standard format, it will need to be translated when the JSONB is
+converted into RFC 8259 text JSON.
+
+
TEXT →
+The element is a JSON string value that does not contain
+any escapes nor any characters that need to be escaped for either SQL or
+JSON. The payload is the UTF8 text representation of the string value.
+The payload does not include string delimiters.
+
+
TEXTJ →
+The element is a JSON string value that contains
+RFC 8259 character escapes (such as "\n" or "\u0020").
+Those escapes will need to be translated into actual UTF8 if this element
+is [json_extract|extracted] into SQL.
+The payload is the UTF8 text representation of the escaped string value.
+The payload does not include string delimiters.
+
+
TEXT5 →
+The element is a JSON string value that contains
+character escapes, including some character escapes that part of JSON5
+and which are not found in the canonical RFC 8259 spec.
+Those escapes will need to be translated into standard JSON prior to
+rendering the JSON as text, or into their actual UTF8 characters if this
+element is [json_extract|extracted] into SQL.
+The payload is the UTF8 text representation of the escaped string value.
+The payload does not include string delimiters.
+
+
TEXTRAW →
+The element is a JSON string value that contains
+UTF8 characters that need to be escaped if this string is rendered into
+standard JSON text.
+The payload does not include string delimiters.
+
+
ARRAY →
+The element is a JSON array. The payload contains
+JSONB elements that comprise values contained within the array.
+
+
OBJECT →
+The element is a JSON object. The payload contains
+pairs of JSONB elements that comprise entries for the JSON object.
+The first element in each pair must be a string (types 7 through 10).
+The second element of each pair may be any types, including nested
+arrays or objects.
+
+
RESERVED-13 →
+Reserved for future expansion. Legacy implements that encounter this
+element type should raise an error.
+
+
RESERVED-14 →
+Reserved for future expansion. Legacy implements that encounter this
+element type should raise an error.
+
+
RESERVED-15 →
+Reserved for future expansion. Legacy implements that encounter this
+element type should raise an error.
+
+
+Element types outside the range of 0 to 12 are reserved for future
+expansion. The current implement raises an error if see an element type
+other than those listed above. However, future versions of SQLite might
+use of the three remaining element types to implement indexing or similar
+optimizations, to speed up lookup against large JSON arrays and/or objects.
+
+### 3.3 Design Rationale For Element Types
+
+A key goal of JSONB is that it should be quick to translate
+to and from text JSON and/or be constructed from SQL values.
+When converting from text into JSONB, we do not want the
+converter subroutine to burn CPU cycles converting elements
+values into some standard format which might never be used.
+Format conversion is "lazy" - it is deferred until actually
+needed. This has implications for the JSONB format design:
+
+ 1. Numeric values are stored as text, not a numbers. The values are
+ a direct copy of the text JSON values from which they are derived.
+
+ 2. There are multiple element types depending on the details of value
+ formats. For example, INT is used for pure RFC-8259 integer
+ literals and INT5 exists for JSON5 extensions such as hexadecimal
+ notation. FLOAT is used for pure RFC-8259 floating point literals
+ and FLOAT5 is used for JSON5 extensions. There are four different
+ representations of strings, depending on where the string came from
+ and how special characters within the string are escaped.
+
+A second goal of JSONB is that it should be capable of serving as the
+"parse tree" for JSON when a JSON value is being processed by the
+various [JSON SQL functions] built into SQLite. Before JSONB was
+developed, operations such [json_replace()] and [json_patch()]
+and similar worked in three stages:
+
+
+ 1. Translate the text JSON into a internal format that is
+ easier to scan and edit.
+ 2. Perform the requested operation on the JSON.
+ 3. Translate the internal format back into text.
+
+JSONB seeks to serve as the internal format directly - bypassing
+the first and third stages of that process. Since most of the CPU
+cycles are spent on the first and third stages, that suggests that
+JSONB processing will be much faster than text JSON processing.
+
+So when processing JSONB, only the second stage of the three-stage
+process is required. But when processing text JSON, it is still necessary
+to do stages one and three. If JSONB is to be used as the internal
+binary representation, this is yet another reason to store numeric
+values as text. Storing numbers as text minimizes the amount of
+conversion work needed for stages one and three. This is also why
+there are four different representations of text in JSONB. Different
+text representations are used for text coming from different sources
+(RFC-8259 JSON, JSON5, or SQL string values) and conversions only
+happen if and when they are actually needed.
+
+### 3.4 Valid JSONB BLOBs
+
+A valid JSONB BLOB consists of a single JSON element. The element must
+exactly fill the BLOB. This one element is often a JSON object or array
+and those usually contain additional elements as its payload, but the
+element can be a primite value such a string, number, boolean, or null.
+
+When the built-in JSON functions are attempting to determine if a BLOB
+argument is a JSONB or just a random BLOB, they look at the header of
+the outer element to see that it is well-formed and that the element
+completely fills the BLOB. If these conditions are met, then the BLOB
+is accepted as a JSONB value.
diff --git a/sqlite/doc/lemon.html b/sqlite/doc/lemon.html
index 773c68e6..4147d9b3 100644
--- a/sqlite/doc/lemon.html
+++ b/sqlite/doc/lemon.html
@@ -2,12 +2,13 @@
The Lemon Parser Generator
-
-
The Lemon Parser Generator
+
+
+
The Lemon Parser Generator
Lemon is an LALR(1) parser generator for C.
It does the same job as "bison" and "yacc".
-But lemon is not a bison or yacc clone. Lemon
+But Lemon is not a bison or yacc clone. Lemon
uses a different grammar syntax which is designed to
reduce the number of coding errors. Lemon also uses a
parsing engine that is faster than yacc and
@@ -16,95 +17,160 @@
The Lemon Parser Generator
has also been updated so that it too can generate a
reentrant and threadsafe parser.)
Lemon also implements features that can be used
-to eliminate resource leaks, making is suitable for use
+to eliminate resource leaks, making it suitable for use
in long-running programs such as graphical user interfaces
or embedded controllers.
This document is an introduction to the Lemon
parser generator.
The language parser code created by Lemon is very robust and
+is well-suited for use in internet-facing applications that need to
+safely process maliciously crafted inputs.
-
The main goal of Lemon is to translate a context free grammar (CFG)
+
The "lemon.exe" command-line tool itself works great when given a valid
+input grammar file and almost always gives helpful
+error messages for malformed inputs. However, it is possible for
+a malicious user to craft a grammar file that will cause
+lemon.exe to crash.
+We do not see this as a problem, as lemon.exe is not intended to be used
+with hostile inputs.
+To summarize:
+
+
+
Parser code generated by lemon → Robust and secure
+
The "lemon.exe" command line tool itself → Not so much
+
+
+
+
3.0 Theory of Operation
+
+
Lemon is computer program that translates a context free grammar (CFG)
for a particular language into C code that implements a parser for
that language.
-The program has two inputs:
+The Lemon program has two inputs:
The grammar specification.
A parser template file.
-Typically, only the grammar specification is supplied by the programmer.
-Lemon comes with a default parser template which works fine for most
-applications. But the user is free to substitute a different parser
-template if desired.
-
-
Depending on command-line options, Lemon will generate between
-one and three files of outputs.
+
Typically, only the grammar specification is supplied by the programmer.
+Lemon comes with a default parser template
+("lempar.c")
+that works fine for most applications. But the user is free to substitute
+a different parser template if desired.
+
+
Depending on command-line options, Lemon will generate up to
+three output files.
-
C code to implement the parser.
-
A header file defining an integer ID for each terminal symbol.
+
C code to implement a parser for the input grammar.
+
A header file defining an integer ID for each terminal symbol
+ (or "token").
An information file that describes the states of the generated parser
automaton.
-By default, all three of these output files are generated.
+
By default, all three of these output files are generated.
The header file is suppressed if the "-m" command-line option is
used and the report file is omitted when "-q" is selected.
The grammar specification file uses a ".y" suffix, by convention.
In the examples used in this document, we'll assume the name of the
grammar file is "gram.y". A typical use of Lemon would be the
-following command:
+following command:
lemon gram.y
-This command will generate three output files named "gram.c",
+
This command will generate three output files named "gram.c",
"gram.h" and "gram.out".
The first is C code to implement the parser. The second
is the header file that defines numerical values for all
terminal symbols, and the last is the report that explains
the states used by the parser automaton.
-
Command Line Options
+
+
3.1 Command Line Options
The behavior of Lemon can be modified using command-line options.
You can obtain a list of the available command-line options together
-with a brief explanation of what each does by typing
+with a brief explanation of what each does by typing
- lemon -?
+ lemon "-?"
-As of this writing, the following command-line options are supported:
+
As of this writing, the following command-line options are supported:
-b
Show only the basis for each parser state in the report file.
-c
-Do not compress the generated action tables.
+Do not compress the generated action tables. The parser will be a
+little larger and slower, but it will detect syntax errors sooner.
+
-ddirectory
+Write all output files into directory. Normally, output files
+are written into the directory that contains the input grammar file.
-Dname
-Define C preprocessor macro name. This macro is useable by
-"%ifdef" lines in the grammar file.
+Define C preprocessor macro name. This macro is usable by
+"%ifdef",
+"%ifndef", and
+"%if lines
+in the grammar file.
+
-E
+Run the "%if" preprocessor step only and print the revised grammar
+file.
-g
Do not generate a parser. Instead write the input grammar to standard
output with all comments, actions, and other extraneous text removed.
-l
-Omit "#line" directives int the generated parser C code.
+Omit "#line" directives in the generated parser C code.
-m
Cause the output C source code to be compatible with the "makeheaders"
-program.
+program.
-p
-Display all conflicts that are resolved by
+Display all conflicts that are resolved by
precedence rules.
-q
Suppress generation of the report file.
-r
Do not sort or renumber the parser states as part of optimization.
-s
-Show parser statistics before existing.
+Show parser statistics before exiting.
-Tfile
Use file as the template for the generated C-code parser implementation.
-x
Print the Lemon version number.
-
The Parser Interface
+
+
3.2 The Parser Interface
Lemon doesn't generate a complete, working program. It only generates
a few subroutines that implement a parser. This section describes
@@ -114,11 +180,11 @@
The Parser Interface
Before a program begins using a Lemon-generated parser, the program
must first create the parser.
-A new parser is created as follows:
+A new parser is created as follows:
void *pParser = ParseAlloc( malloc );
-The ParseAlloc() routine allocates and initializes a new parser and
+
The ParseAlloc() routine allocates and initializes a new parser and
returns a pointer to it.
The actual data structure used to represent a parser is opaque —
its internal structure is not visible or usable by the calling routine.
@@ -128,24 +194,24 @@
The Parser Interface
subroutine used to allocate memory. Typically this means malloc().
After a program is finished using a parser, it can reclaim all
-memory allocated by that parser by calling
+memory allocated by that parser by calling
ParseFree(pParser, free);
-The first argument is the same pointer returned by ParseAlloc(). The
+
The first argument is the same pointer returned by ParseAlloc(). The
second argument is a pointer to the function used to release bulk
memory back to the system.
After a parser has been allocated using ParseAlloc(), the programmer
must supply the parser with a sequence of tokens (terminal symbols) to
be parsed. This is accomplished by calling the following function
-once for each token:
+once for each token:
Parse(pParser, hTokenID, sTokenData, pArg);
-The first argument to the Parse() routine is the pointer returned by
+
The first argument to the Parse() routine is the pointer returned by
ParseAlloc().
-The second argument is a small positive integer that tells the parse the
+The second argument is a small positive integer that tells the parser the
type of the next token in the data stream.
There is one token type for each terminal symbol in the grammar.
The gram.h file generated by Lemon contains #define statements that
@@ -153,7 +219,7 @@
The Parser Interface
A value of 0 for the second argument is a special flag to the
parser to indicate that the end of input has been reached.
The third argument is the value of the given token. By default,
-the type of the third argument is integer, but the grammar will
+the type of the third argument is "void*", but the grammar will
usually redefine this type to be some kind of structure.
Typically the second argument will be a broad category of tokens
such as "identifier" or "number" and the third argument will
@@ -161,7 +227,7 @@
The Parser Interface
The Parse() function may have either three or four arguments,
depending on the grammar. If the grammar specification file requests
-it (via the extra_argument directive),
+it (via the %extra_argument directive),
the Parse() function will have a fourth parameter that can be
of any type chosen by the programmer. The parser doesn't do anything
with this argument except to pass it through to action routines.
@@ -169,40 +235,40 @@
The Parser Interface
to the action routines without having to use global variables.
A typical use of a Lemon parser might look something like the
-following:
-
-This example shows a user-written routine that parses a file of
+
This example shows a user-written routine that parses a file of
text and returns a pointer to the parse tree.
(All error-handling code is omitted from this example to keep it
simple.)
We assume the existence of some kind of tokenizer which is created
using TokenizerCreate() on line 8 and deleted by TokenizerFree()
on line 16. The GetNextToken() function on line 11 retrieves the
-next token from the input file and puts its type in the
+next token from the input file and puts its type in the
integer variable hTokenId. The sToken variable is assumed to be
some kind of structure that contains details about each token,
-such as its complete text, what line it occurs on, etc.
+such as its complete text, what line it occurs on, etc.
-
This example also assumes the existence of structure of type
+
This example also assumes the existence of a structure of type
ParserState that holds state information about a particular parse.
An instance of such a structure is created on line 6 and initialized
on line 10. A pointer to this structure is passed into the Parse()
@@ -213,18 +279,18 @@
The Parser Interface
the ParserState structure is left pointing to the root of the parse
tree.
-
The core of this example as it relates to Lemon is as follows:
+
The core of this example as it relates to Lemon is as follows:
-Basically, what a program has to do to use a Lemon-generated parser
+
Basically, what a program has to do to use a Lemon-generated parser
is first create the parser, then send it lots of tokens obtained by
tokenizing an input source. When the end of input is reached, the
Parse() routine should be called one last time with a token type
@@ -235,21 +301,75 @@
The Parser Interface
There is one other interface routine that should be mentioned
before we move on.
The ParseTrace() function can be used to generate debugging output
-from the parser. A prototype for this routine is as follows:
+from the parser. A prototype for this routine is as follows:
ParseTrace(FILE *stream, char *zPrefix);
-After this routine is called, a short (one-line) message is written
+
After this routine is called, a short (one-line) message is written
to the designated output stream every time the parser changes states
or calls an action routine. Each such message is prefaced using
the text given by zPrefix. This debugging output can be turned off
by calling ParseTrace() again with a first argument of NULL (0).
-
Differences With YACC and BISON
+
+
3.2.1 Allocating The Parse Object On Stack
+
+
If all calls to the Parse() interface are made from within
+%code directives, then the parse
+object can be allocated from the stack rather than from the heap.
+These are the steps:
+
+
+
Declare a local variable of type "yyParser"
+
Initialize the variable using ParseInit()
+
Pass a pointer to the variable in calls to Parse()
+
Deallocate substructure in the parse variable using ParseFinalize().
+
+
+
The following code illustrates how this is done:
+
+
Use the %extra_argument directive
+to specify the type and name of the 4th parameter to the
+Parse() function.
+
+
+
+
3.3 Differences With YACC and BISON
Programmers who have previously used the yacc or bison parser
generator will notice several important differences between yacc and/or
-bison and Lemon.
+bison and Lemon.
In yacc and bison, the parser calls the tokenizer. In Lemon,
the tokenizer calls the parser.
@@ -258,7 +378,7 @@
Differences With YACC and BISON
Lemon allows multiple parsers to be running simultaneously. Yacc
and bison do not.
-These differences may cause some initial confusion for programmers
+
These differences may cause some initial confusion for programmers
with prior yacc and bison experience.
But after years of experience using Lemon, I firmly
believe that the Lemon way of doing things is better.
@@ -266,10 +386,39 @@
Differences With YACC and BISON
Updated as of 2016-02-16:
The text above was written in the 1990s.
We are told that Bison has lately been enhanced to support the
-tokenizer-calls-parser paradigm used by Lemon, and to obviate the
+tokenizer-calls-parser paradigm used by Lemon, eliminating the
need for global variables.
On Windows machines with Visual C++ installed, bring up a
+"VS20NN x64 Native Tools Command Prompt" window and enter:
+
+
+cl lemon.c
+
+
+
Compiling Lemon really is that simple.
+Additional compiler options such as
+"-O2" or "-g" or "-Wall" can be added if desired, but they are not
+necessary.
+
+
+
+
4.0 Input File Syntax
The main purpose of the grammar specification file for Lemon is
to define the grammar for the parser. But the input file also
@@ -277,25 +426,26 @@
Input File Syntax
Most of the work in using Lemon is in writing an appropriate
grammar file.
-
The grammar file for lemon is, for the most part, free format.
+
The grammar file for Lemon is, for the most part, a free format.
It does not have sections or divisions like yacc or bison. Any
-declaration can occur at any point in the file.
-Lemon ignores whitespace (except where it is needed to separate
-tokens) and it honors the same commenting conventions as C and C++.
+declaration can occur at any point in the file. Lemon ignores
+whitespace (except where it is needed to separate tokens), and it
+honors the same commenting conventions as C and C++.
-
Terminals and Nonterminals
+
+
4.1 Terminals and Nonterminals
A terminal symbol (token) is any string of alphanumeric
and/or underscore characters
-that begins with an upper case letter.
+that begins with an uppercase letter.
A terminal can contain lowercase letters after the first character,
-but the usual convention is to make terminals all upper case.
+but the usual convention is to make terminals all uppercase.
A nonterminal, on the other hand, is any string of alphanumeric
-and underscore characters than begins with a lower case letter.
-Again, the usual convention is to make nonterminals use all lower
-case letters.
+and underscore characters than begins with a lowercase letter.
+Again, the usual convention is to make nonterminals use all lowercase
+letters.
-
In Lemon, terminal and nonterminal symbols do not need to
+
In Lemon, terminal and nonterminal symbols do not need to
be declared or identified in a separate section of the grammar file.
Lemon is able to generate a list of all terminals and nonterminals
by examining the grammar rules, and it can always distinguish a
@@ -308,7 +458,8 @@
Terminals and Nonterminals
terminal symbols. With Lemon, all symbols, terminals and nonterminals,
must have alphanumeric names.
-
Grammar Rules
+
+
4.2 Grammar Rules
The main component of a Lemon grammar file is a sequence of grammar
rules.
@@ -319,15 +470,15 @@
Grammar Rules
rule can be empty.
Rules can occur in any order, except that the left-hand side of the
first rule is assumed to be the start symbol for the grammar (unless
-specified otherwise using the %start directive described below.)
-A typical sequence of grammar rules might look something like this:
+specified otherwise using the %start_symbol
+directive described below.)
+A typical sequence of grammar rules might look something like this:
expr ::= expr PLUS expr.
expr ::= expr TIMES expr.
expr ::= LPAREN expr RPAREN.
expr ::= VALUE.
-
There is one non-terminal in this example, "expr", and five
terminal symbols or tokens: "PLUS", "TIMES", "LPAREN",
@@ -339,11 +490,10 @@
Grammar Rules
In Lemon, this action is specified by putting the C code (contained
within curly braces {...}) immediately after the
period that closes the rule.
-For example:
+For example:
expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
-
In order to be useful, grammar actions must normally be linked to
their associated grammar rules.
@@ -360,18 +510,18 @@
Grammar Rules
Lemon avoids the need to count grammar symbols by assigning symbolic
names to each symbol in a grammar rule and then using those symbolic
names in the action.
-In yacc or bison, one would write this:
+In yacc or bison, one would write this:
-But in Lemon, the same rule becomes the following:
+
But in Lemon, the same rule becomes the following:
expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
-In the Lemon rule, any symbol in parentheses after a grammar rule
+
In the Lemon rule, any symbol in parentheses after a grammar rule
symbol becomes a place holder for that symbol in the grammar rule.
This place holder can then be used in the associated C action to
-stand for the value of that symbol.
+stand for the value of that symbol.
The Lemon notation for linking a grammar rule with its reduce
action is superior to yacc/bison on several counts.
@@ -381,11 +531,11 @@
Grammar Rules
includes a linking symbol in parentheses but that linking symbol
is not actually used in the reduce action, then an error message
is generated.
-For example, the rule
+For example, the rule
expr(A) ::= expr(B) PLUS expr(C). { A = B; }
-will generate an error because the linking symbol "C" is used
+
will generate an error because the linking symbol "C" is used
in the grammar rule but not in the reduce action.
The Lemon notation for linking grammar rules to reduce actions
@@ -393,8 +543,8 @@
Grammar Rules
allocated by the values of terminals and nonterminals on the
right-hand side of a rule.
-
-
Precedence Rules
+
+
4.3 Precedence Rules
Lemon resolves parsing ambiguities in exactly the same way as
yacc and bison. A shift-reduce conflict is resolved in favor
@@ -402,73 +552,73 @@
Precedence Rules
whichever rule comes first in the grammar file.
Just like in
-yacc and bison, Lemon allows a measure of control
-over the resolution of paring conflicts using precedence rules.
+yacc and bison, Lemon allows a measure of control
+over the resolution of parsing conflicts using precedence rules.
A precedence value can be assigned to any terminal symbol
-using the
-%left,
-%right or
-%nonassoc directives. Terminal symbols
-mentioned in earlier directives have a lower precedence that
+using the
+%left,
+%right or
+%nonassoc directives. Terminal symbols
+mentioned in earlier directives have a lower precedence than
terminal symbols mentioned in later directives. For example:
-
+
%left AND.
%left OR.
%nonassoc EQ NE GT GE LT LE.
%left PLUS MINUS.
%left TIMES DIVIDE MOD.
%right EXP NOT.
-
+
In the preceding sequence of directives, the AND operator is
defined to have the lowest precedence. The OR operator is one
precedence level higher. And so forth. Hence, the grammar would
-attempt to group the ambiguous expression
+attempt to group the ambiguous expression
a AND b OR c
-like this
+
like this
a AND (b OR c).
-The associativity (left, right or nonassoc) is used to determine
+
The associativity (left, right or nonassoc) is used to determine
the grouping when the precedence is the same. AND is left-associative
-in our example, so
+in our example, so
a AND b AND c
-is parsed like this
+
is parsed like this
(a AND b) AND c.
-The EXP operator is right-associative, though, so
+
The EXP operator is right-associative, though, so
a EXP b EXP c
-is parsed like this
+
is parsed like this
a EXP (b EXP c).
-The nonassoc precedence is used for non-associative operators.
-So
+
The nonassoc precedence is used for non-associative operators.
+So
a EQ b EQ c
-is an error.
+
is an error.
The precedence of non-terminals is transferred to rules as follows:
The precedence of a grammar rule is equal to the precedence of the
left-most terminal symbol in the rule for which a precedence is
defined. This is normally what you want, but in those cases where
-you want to precedence of a grammar rule to be something different,
+you want the precedence of a grammar rule to be something different,
you can specify an alternative precedence symbol by putting the
symbol in square braces after the period at the end of the rule and
before any C-code. For example:
-
+
expr = MINUS expr. [NOT]
-
+
This rule has a precedence equal to that of the NOT symbol, not the
MINUS symbol as would have been the case by default.
@@ -477,7 +627,7 @@
Precedence Rules
symbols and individual
grammar rules, we can now explain precisely how parsing conflicts
are resolved in Lemon. Shift-reduce conflicts are resolved
-as follows:
+as follows:
If either the token to be shifted or the rule to be reduced
lacks precedence information, then resolve in favor of the
@@ -485,137 +635,145 @@
Precedence Rules
If the precedence of the token to be shifted is greater than
the precedence of the rule to reduce, then resolve in favor
of the shift. No parsing conflict is reported.
-
If the precedence of the token it be shifted is less than the
+
If the precedence of the token to be shifted is less than the
precedence of the rule to reduce, then resolve in favor of the
reduce action. No parsing conflict is reported.
If the precedences are the same and the shift token is
right-associative, then resolve in favor of the shift.
No parsing conflict is reported.
-
If the precedences are the same the shift token is
+
If the precedences are the same and the shift token is
left-associative, then resolve in favor of the reduce.
No parsing conflict is reported.
-
Otherwise, resolve the conflict by doing the shift and
- report the parsing conflict.
+
Otherwise, resolve the conflict by doing the shift, and
+ report a parsing conflict.
-Reduce-reduce conflicts are resolved this way:
+
Reduce-reduce conflicts are resolved this way:
-
If either reduce rule
+
If either reduce rule
lacks precedence information, then resolve in favor of the
- rule that appears first in the grammar and report a parsing
+ rule that appears first in the grammar, and report a parsing
conflict.
-
If both rules have precedence and the precedence is different
+
If both rules have precedence and the precedence is different,
then resolve the dispute in favor of the rule with the highest
- precedence and do not report a conflict.
+ precedence, and do not report a conflict.
Otherwise, resolve the conflict by reducing by the rule that
- appears first in the grammar and report a parsing conflict.
+ appears first in the grammar, and report a parsing conflict.
-
Special Directives
+
+
4.4 Special Directives
The input grammar to Lemon consists of grammar rules and special
directives. We've described all the grammar rules, so now we'll
talk about the special directives.
-
Directives in lemon can occur in any order. You can put them before
-the grammar rules, or after the grammar rules, or in the mist of the
+
Directives in Lemon can occur in any order. You can put them before
+the grammar rules, or after the grammar rules, or in the midst of the
grammar rules. It doesn't matter. The relative order of
directives used to assign precedence to terminals is important, but
other than that, the order of directives in Lemon is arbitrary.
-
Lemon supports the following special directives:
+
-Each of these directives will be described separately in the
+
Each of these directives will be described separately in the
following sections:
-
-
The %code directive
+
+
4.4.1 The %code directive
-
The %code directive is used to specify addition C code that
+
The %code directive is used to specify additional C code that
is added to the end of the main output file. This is similar to
-the %include directive except that %include
-is inserted at the beginning of the main output file.
+the %include directive except that
+%include is inserted at the beginning of the main output file.
-
%code is typically used to include some action routines or perhaps
-a tokenizer or even the "main()" function
+
%code is typically used to include some action routines or perhaps
+a tokenizer or even the "main()" function
as part of the output file.
-
-
The %default_destructor directive
+
There can be multiple %code directives. The arguments of
+all %code directives are concatenated.
+
+
+
4.4.2 The %default_destructor directive
-
The %default_destructor directive specifies a destructor to
+
The %default_destructor directive specifies a destructor to
use for non-terminals that do not have their own destructor
-specified by a separate %destructor directive. See the documentation
-on the %destructor directive below for
+specified by a separate %destructor directive. See the documentation
+on the %destructor directive below for
additional information.
-
In some grammers, many different non-terminal symbols have the
-same datatype and hence the same destructor. This directive is
-a convenience way to specify the same destructor for all those
+
In some grammars, many different non-terminal symbols have the
+same data type and hence the same destructor. This directive is
+a convenient way to specify the same destructor for all those
non-terminals using a single statement.
-
-
The %default_type directive
+
+
4.4.3 The %default_type directive
-
The %default_type directive specifies the datatype of non-terminal
-symbols that do no have their own datatype defined using a separate
-%type directive.
-
+
The %default_type directive specifies the data type of non-terminal
+symbols that do not have their own data type defined using a separate
+%type directive.
-
-
The %destructor directive
+
+
4.4.4 The %destructor directive
-
The %destructor directive is used to specify a destructor for
+
The %destructor directive is used to specify a destructor for
a non-terminal symbol.
-(See also the %token_destructor
+(See also the %token_destructor
directive which is used to specify a destructor for terminal symbols.)
A non-terminal's destructor is called to dispose of the
non-terminal's value whenever the non-terminal is popped from
-the stack. This includes all of the following circumstances:
+the stack. This includes all of the following circumstances:
When a rule reduces and the value of a non-terminal on
the right-hand side is not linked to C code.
When the stack is popped during error processing.
When the ParseFree() function runs.
-The destructor can do whatever it wants with the value of
+
The destructor can do whatever it wants with the value of
the non-terminal, but its design is to deallocate memory
or other resources held by that non-terminal.
-
Consider an example:
+
Consider an example:
%type nt {void*}
%destructor nt { free($$); }
nt(A) ::= ID NUM. { A = malloc( 100 ); }
-This example is a bit contrived but it serves to illustrate how
+
This example is a bit contrived, but it serves to illustrate how
destructors work. The example shows a non-terminal named
"nt" that holds values of type "void*". When the rule for
an "nt" reduces, it sets the value of the non-terminal to
@@ -631,281 +789,364 @@
The %destructor directive
the non-terminal is used by C-code, then it is assumed that the
C-code will take care of destroying it.
More commonly, the value is used to build some
-larger structure and we don't want to destroy it, which is why
+larger structure, and we don't want to destroy it, which is why
the destructor is not called in this circumstance.
Destructors help avoid memory leaks by automatically freeing
allocated objects when they go out of scope.
To do the same using yacc or bison is much more difficult.
-
-
The %extra_argument directive
+
+
4.4.5 The %extra_argument directive
-The %extra_argument directive instructs Lemon to add a 4th parameter
+
The %extra_argument directive instructs Lemon to add a 4th parameter
to the parameter list of the Parse() function it generates. Lemon
doesn't do anything itself with this extra argument, but it does
make the argument available to C-code action routines, destructors,
and so forth. For example, if the grammar file contains:
-
+
%extra_argument { MyStruct *pAbc }
-
+
Then the Parse() function generated will have an 4th parameter
of type "MyStruct*" and all action routines will have access to
a variable named "pAbc" that is the value of the 4th parameter
in the most recent call to Parse().
-
-
The %fallback directive
+
The %extra_context directive works the same except that it
+is passed in on the ParseAlloc() or ParseInit() routines instead of
+on Parse().
+
+
+
4.4.6 The %extra_context directive
+
+
The %extra_context directive instructs Lemon to add a 2nd parameter
+to the parameter list of the ParseAlloc() and ParseInit() functions. Lemon
+doesn't do anything itself with these extra argument, but it does
+store the value make it available to C-code action routines, destructors,
+and so forth. For example, if the grammar file contains:
+
+
+ %extra_context { MyStruct *pAbc }
+
+
+
Then the ParseAlloc() and ParseInit() functions will have an 2nd parameter
+of type "MyStruct*" and all action routines will have access to
+a variable named "pAbc" that is the value of that 2nd parameter.
+
+
The %extra_argument directive works the same except that it
+is passed in on the Parse() routine instead of on ParseAlloc()/ParseInit().
-
The %fallback directive specifies an alternative meaning for one
+
+
4.4.7 The %fallback directive
+
+
The %fallback directive specifies an alternative meaning for one
or more tokens. The alternative meaning is tried if the original token
-would have generated a syntax error.
+would have generated a syntax error.
-
The %fallback directive was added to support robust parsing of SQL
-syntax in SQLite.
+
The %fallback directive was added to support robust parsing of SQL
+syntax in SQLite.
The SQL language contains a large assortment of keywords, each of which
appears as a different token to the language parser. SQL contains so
-many keywords, that it can be difficult for programmers to keep up with
+many keywords that it can be difficult for programmers to keep up with
them all. Programmers will, therefore, sometimes mistakenly use an
-obscure language keyword for an identifier. The %fallback directive
+obscure language keyword for an identifier. The %fallback directive
provides a mechanism to tell the parser: "If you are unable to parse
-this keyword, try treating it as an identifier instead."
+this keyword, try treating it as an identifier instead."
-
The syntax of %fallback is as follows:
+
The syntax of %fallback is as follows:
-%fallbackIDTOKEN....
-
+%fallbackIDTOKEN....
+
-
In words, the %fallback directive is followed by a list of token names
-terminated by a period. The first token name is the fallback token - the
+
In words, the %fallback directive is followed by a list of token
+names terminated by a period.
+The first token name is the fallback token — the
token to which all the other tokens fall back to. The second and subsequent
arguments are tokens which fall back to the token identified by the first
-argument.
+argument.
-
-
The %ifdef, %ifndef, and %endif directives.
+
+
4.4.8 The %if directive and its friends
-
The %ifdef, %ifndef, and %endif directives are similar to
-#ifdef, #ifndef, and #endif in the C-preprocessor, just not as general.
+
The %if, %ifdef, %ifndef, %else,
+and %endif directives
+are similar to #if, #ifdef, #ifndef, #else, and #endif in the C-preprocessor,
+just not as general.
Each of these directives must begin at the left margin. No whitespace
-is allowed between the "%" and the directive name.
+is allowed between the "%" and the directive name.
-
Grammar text in between "%ifdef MACRO" and the next nested "%endif" is
+
Grammar text in between "%ifdef MACRO" and the next nested
+"%endif" is
ignored unless the "-DMACRO" command-line option is used. Grammar text
-betwen "%ifndef MACRO" and the next nested "%endif" is included except when
-the "-DMACRO" command-line option is used.
+betwen "%ifndef MACRO" and the next nested "%endif" is
+included except when the "-DMACRO" command-line option is used.
+
+
The text in between "%ifCONDITIONAL" and its
+corresponding %endif is included only if CONDITIONAL
+is true. The CONDITION is one or more macro names, optionally connected
+using the "||" and "&&" binary operators, the "!" unary operator,
+and grouped using balanced parentheses. Each term is true if the
+corresponding macro exists, and false if it does not exist.
-
Note that the argument to %ifdef and %ifndef must be a single
-preprocessor symbol name, not a general expression. There is no "%else"
-directive.
+
An optional "%else" directive can occur anywhere in between a
+%ifdef, %ifndef, or %if directive and
+its corresponding %endif.
+
Note that the argument to %ifdef and %ifndef is
+intended to be a single preprocessor symbol name, not a general expression.
+Use the "%if" directive for general expressions.
-
-
The %include directive
+
+
4.4.9 The %include directive
-
The %include directive specifies C code that is included at the
-top of the generated parser. You can include any text you want --
+
The %include directive specifies C code that is included at the
+top of the generated parser. You can include any text you want —
the Lemon parser generator copies it blindly. If you have multiple
-%include directives in your grammar file, their values are concatenated
-so that all %include code ultimately appears near the top of the
-generated parser, in the same order as it appeared in the grammer.
+%include directives in your grammar file, their values are concatenated
+so that all %include code ultimately appears near the top of the
+generated parser, in the same order as it appeared in the grammar.
-
The %include directive is very handy for getting some extra #include
+
The %include directive is very handy for getting some extra #include
preprocessor statements at the beginning of the generated parser.
For example:
-
+
%include {#include <unistd.h>}
-
+
This might be needed, for example, if some of the C actions in the
-grammar call functions that are prototyed in unistd.h.
+grammar call functions that are prototyped in unistd.h.
+
+
Use the %code directive to add code to
+the end of the generated parser.
-
-
The %left directive
+
+
4.4.10 The %left directive
-The %left directive is used (along with the %right and
-%nonassoc directives) to declare precedences of
-terminal symbols. Every terminal symbol whose name appears after
-a %left directive but before the next period (".") is
+The %left directive is used (along with the
+%right and
+%nonassoc directives) to declare
+precedences of terminal symbols.
+Every terminal symbol whose name appears after
+a %left directive but before the next period (".") is
given the same left-associative precedence value. Subsequent
-%left directives have higher precedence. For example:
+%left directives have higher precedence. For example:
-
+
%left AND.
%left OR.
%nonassoc EQ NE GT GE LT LE.
%left PLUS MINUS.
%left TIMES DIVIDE MOD.
%right EXP NOT.
-
+
-
Note the period that terminates each %left, %right or %nonassoc
+
Note the period that terminates each %left,
+%right or %nonassoc
directive.
LALR(1) grammars can get into a situation where they require
a large amount of stack space if you make heavy use or right-associative
-operators. For this reason, it is recommended that you use %left
-rather than %right whenever possible.
+operators. For this reason, it is recommended that you use %left
+rather than %right whenever possible.
-
-
The %name directive
+
+
4.4.11 The %name directive
By default, the functions generated by Lemon all begin with the
five-character string "Parse". You can change this string to something
-different using the %name directive. For instance:
+different using the %name directive. For instance:
-
+
%name Abcde
-
+
Putting this directive in the grammar file will cause Lemon to generate
-functions named
+functions named
AbcdeAlloc(),
AbcdeFree(),
AbcdeTrace(), and
Abcde().
-The %name directive allows you to generator two or more different
-parsers and link them all into the same executable.
-
+The %name directive allows you to generate two or more different
+parsers and link them all into the same executable.
-
-
The %nonassoc directive
+
+
4.4.12 The %nonassoc directive
This directive is used to assign non-associative precedence to
-one or more terminal symbols. See the section on
+one or more terminal symbols. See the section on
precedence rules
-or on the %left directive for additional information.
+or on the %left directive
+for additional information.
-
-
The %parse_accept directive
+
+
4.4.13 The %parse_accept directive
-
The %parse_accept directive specifies a block of C code that is
+
The %parse_accept directive specifies a block of C code that is
executed whenever the parser accepts its input string. To "accept"
an input string means that the parser was able to process all tokens
without error.
The %parse_failure directive specifies a block of C code that
+
The %parse_failure directive specifies a block of C code that
is executed whenever the parser fails complete. This code is not
executed until the parser has tried and failed to resolve an input
error using is usual error recovery strategy. The routine is
only invoked when parsing is unable to continue.
-
+
%parse_failure {
fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
}
-
+
-
-
The %right directive
+
+
4.4.15 The %right directive
This directive is used to assign right-associative precedence to
-one or more terminal symbols. See the section on
+one or more terminal symbols. See the section on
precedence rules
or on the %left directive for additional information.
-
-
The %stack_overflow directive
+
+
4.4.16 The %stack_overflow directive
-
The %stack_overflow directive specifies a block of C code that
+
The %stack_overflow directive specifies a block of C code that
is executed if the parser's internal stack ever overflows. Typically
this just prints an error message. After a stack overflow, the parser
will be unable to continue and must be reset.
You can help prevent parser stack overflows by avoiding the use
of right recursion and right-precedence operators in your grammar.
-Use left recursion and and left-precedence operators instead, to
+Use left recursion and and left-precedence operators instead to
encourage rules to reduce sooner and keep the stack size down.
-For example, do rules like this:
+For example, do rules like this:
list ::= list element. // left-recursion. Good!
list ::= .
-Not like this:
+
Not like this:
list ::= element list. // right-recursion. Bad!
list ::= .
-
-
The %stack_size directive
+
+
4.4.17 The %stack_size directive
If stack overflow is a problem and you can't resolve the trouble
by using left-recursion, then you might want to increase the size
of the parser's stack using this directive. Put an positive integer
-after the %stack_size directive and Lemon will generate a parse
+after the %stack_size directive and Lemon will generate a parse
with a stack of the requested size. The default value is 100.
-
+
%stack_size 2000
-
+
-
-
The %start_symbol directive
+
+
4.4.18 The %start_symbol directive
-
By default, the start-symbol for the grammar that Lemon generates
+
By default, the start symbol for the grammar that Lemon generates
is the first non-terminal that appears in the grammar file. But you
-can choose a different start-symbol using the %start_symbol directive.
+can choose a different start symbol using the
+%start_symbol directive.
-
Tokens are normally created automatically, the first time they are used.
+Any identifier that begins with an upper-case letter is a token.
-
-
The %token_destructor directive
+
Sometimes it is useful to declare tokens in advance, however. The
+integer values assigned to each token determined by the order in which
+the tokens are seen. So by declaring tokens in advance, it is possible to
+cause some tokens to have low-numbered values, which might be desirable in
+some grammers, or to have sequential values assigned to a sequence of
+related tokens. For this reason, the %token directive is provided to
+declare tokens in advance. The syntax is as follows:
-
The %destructor directive assigns a destructor to a non-terminal
-symbol. (See the description of the %destructor directive above.)
-This directive does the same thing for all terminal symbols.
+
+%tokenTOKENTOKEN....
+
+
+
The %token directive is followed by zero or more token symbols and
+terminated by a single ".". Each token named is created if it does not
+already exist. Tokens are created in order.
+
+
+
+
4.4.21 The %token_class directive
+
+
Undocumented. Appears to be related to the MULTITERMINAL concept.
+Implementation.
+
+
+
4.4.22 The %token_destructor directive
+
+
The %destructor directive assigns a destructor to a non-terminal
+symbol. (See the description of the
+%destructor directive above.)
+The %token_destructor directive does the same thing
+for all terminal symbols.
-
Unlike non-terminal symbols which may each have a different data type
+
Unlike non-terminal symbols, which may each have a different data type
for their values, terminals all use the same data type (defined by
-the %token_type directive) and so they use a common destructor. Other
-than that, the token destructor works just like the non-terminal
+the %token_type directive)
+and so they use a common destructor.
+Other than that, the token destructor works just like the non-terminal
destructors.
-
-
The %token_prefix directive
+
+
4.4.23 The %token_prefix directive
Lemon generates #defines that assign small integer constants
to each terminal symbol in the grammar. If desired, Lemon will
add a prefix specified by this directive
-to each of the #defines it generates.
-So if the default output of Lemon looked like this:
+to each of the #defines it generates.
+
+
So if the default output of Lemon looked like this:
#define AND 1
#define MINUS 2
#define OR 3
#define PLUS 4
-You can insert a statement into the grammar like this:
+
You can insert a statement into the grammar like this:
%token_prefix TOKEN_
-to cause Lemon to produce these symbols instead:
+
These directives are used to specify the data types for values
on the parser's stack associated with terminal and non-terminal
symbols. The values of all terminal symbols must be of the same
type. This turns out to be the same data type as the 3rd parameter
to the Parse() function generated by Lemon. Typically, you will
-make the value of a terminal symbol by a pointer to some kind of
+make the value of a terminal symbol be a pointer to some kind of
token structure. Like this:
-
+
%token_type {Token*}
-
+
If the data type of terminals is not specified, the default value
-is "int".
+is "void*".
Non-terminal symbols can each have their own data types. Typically
-the data type of a non-terminal is a pointer to the root of a parse-tree
+the data type of a non-terminal is a pointer to the root of a parse tree
structure that contains all information about that non-terminal.
For example:
-
+
%type expr {Expr*}
-
+
Each entry on the parser's stack is actually a union containing
instances of all data types for every non-terminal and terminal symbol.
@@ -950,38 +1191,92 @@
The %token_type and %type directives
entry parser stack will require 100K of heap space. If you are willing
and able to pay that price, fine. You just need to know.
-
-
The %wildcard directive
+
+
4.4.25 The %wildcard directive
-
The %wildcard directive is followed by a single token name and a
-period. This directive specifies that the identified token should
-match any input token.
+
The %wildcard directive is followed by a single token name and a
+period. This directive specifies that the identified token should
+match any input token.
When the generated parser has the choice of matching an input against
the wildcard token and some other token, the other token is always used.
-The wildcard token is only matched if there are no other alternatives.
+The wildcard token is only matched if there are no alternatives.
+
+
+
4.4.26 The %realloc and %free directives
-
Error Processing
+
The %realloc and %free directives defines function
+that allocate and free heap memory. The signatures of these functions
+should be the same as the realloc() and free() functions from the standard
+C library.
+
+
If both of these functions are defined
+then these functions are used to allocate and free
+memory for supplemental parser stack space, if the initial
+parse stack space is exceeded. The initial parser stack size
+is specified by either %stack_size or the
+-DYYSTACKDEPTH compile-time flag.
+
+
+
5.0 Error Processing
After extensive experimentation over several years, it has been
discovered that the error recovery strategy used by yacc is about
as good as it gets. And so that is what Lemon uses.
When a Lemon-generated parser encounters a syntax error, it
-first invokes the code specified by the %syntax_error directive, if
+first invokes the code specified by the %syntax_error directive, if
any. It then enters its error recovery strategy. The error recovery
strategy is to begin popping the parsers stack until it enters a
state where it is permitted to shift a special non-terminal symbol
named "error". It then shifts this non-terminal and continues
-parsing. But the %syntax_error routine will not be called again
+parsing. The %syntax_error routine will not be called again
until at least three new tokens have been successfully shifted.
If the parser pops its stack until the stack is empty, and it still
-is unable to shift the error symbol, then the %parse_failed routine
+is unable to shift the error symbol, then the
+%parse_failure routine
is invoked and the parser resets itself to its start state, ready
to begin parsing a new file. This is what will happen at the very
-first syntax error, of course, if there are no instances of the
+first syntax error, of course, if there are no instances of the
"error" non-terminal in your grammar.
+
+
+
6.0 History of Lemon
+
+
Lemon was originally written by Richard Hipp sometime in the late
+1980s on a Sun4 Workstation using K&R C.
+There was a companion LL(1) parser generator program named "Lime".
+The Lime source code has been lost.
+
+
The lemon.c source file was originally many separate files that were
+compiled together to generate the "lemon" executable. Sometime in the
+1990s, the individual source code files were combined together into
+the current single large "lemon.c" source file. You can still see traces
+of original filenames in the code.
+
+
Since 2001, Lemon has been part of the
+SQLite project and the source code
+to Lemon has been managed as a part of the
+SQLite source tree in the following
+files:
All of the source code to Lemon, including the template parser file
+"lempar.c" and this documentation file ("lemon.html") are in the public
+domain. You can use the code for any purpose and without attribution.
+
+
The code comes with no warranty. If it breaks, you get to keep both
+pieces.
+
diff --git a/sqlite/doc/testrunner.md b/sqlite/doc/testrunner.md
new file mode 100644
index 00000000..d0248573
--- /dev/null
+++ b/sqlite/doc/testrunner.md
@@ -0,0 +1,356 @@
+
+
+# The testrunner.tcl Script
+
+
+
+
+# 1. Overview
+
+testrunner.tcl is a Tcl script used to run multiple SQLite tests using
+multiple jobs. It supports the following types of tests:
+
+ * Tcl test scripts.
+
+ * Tests run with `make` commands. Examples:
+ - `make mdevtest`
+ - `make releasetest`
+ - `make sdevtest`
+ - `make testrunner`
+
+testrunner.tcl pipes the output of all tests and builds run into log file
+**testrunner.log**, created in the current working directory. Search this
+file to find details of errors. Suggested search commands:
+
+ * `grep "^!" testrunner.log`
+ * `grep failed testrunner.log`
+
+testrunner.tcl also populates SQLite database **testrunner.db**. This database
+contains details of all tests run, running and to be run. A useful query
+might be:
+
+```
+ SELECT * FROM script WHERE state='failed'
+```
+
+Running the command:
+
+```
+ ./testfixture $(TESTDIR)/testrunner.tcl status
+```
+
+in the directory containing the testrunner.db database runs various queries
+to produce a succinct report on the state of a running testrunner.tcl script.
+Running:
+
+```
+ watch ./testfixture $(TESTDIR)/testrunner.tcl status
+```
+
+in another terminal is a good way to keep an eye on a long running test.
+
+Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
+to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
+other binaries in specific configurations to test (see "Source Tests").
+
+
+# 2. Binary Tests
+
+The commands described in this section all run various combinations of the Tcl
+test scripts using the `testfixture` binary used to run the testrunner.tcl
+script (i.e. they do not invoke the compiler to build new binaries, or the
+`make` command to run tests that are not Tcl scripts). The procedure to run
+these tests is therefore:
+
+ 1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
+ whatever method seems convenient.
+
+ 2. Test the binary built in step 1 by running testrunner.tcl with it,
+ perhaps with various options.
+
+The following sub-sections describe the various options that can be
+passed to testrunner.tcl to test binary testfixture builds.
+
+
+## 2.1. Organization of Tcl Tests
+
+Tcl tests are stored in files that match the pattern *\*.test*. They are
+found in both the $TOP/test/ directory, and in the various sub-directories
+of the $TOP/ext/ directory of the source tree. Not all *\*.test* files
+contain Tcl tests - a handful are Tcl scripts designed to invoke other
+*\*.test* files.
+
+The **veryquick** set of tests is a subset of all Tcl test scripts in the
+source tree. In includes most tests, but excludes some that are very slow.
+Almost all fault-injection tests (those that test the response of the library
+to OOM or IO errors) are excluded. It is defined in source file
+*test/permutations.test*.
+
+The **full** set of tests includes all Tcl test scripts in the source tree.
+To run a "full" test is to run all Tcl test scripts that can be found in the
+source tree.
+
+File *permutations.test* defines various test "permutations". A permutation
+consists of:
+
+ * A subset of Tcl test scripts, and
+
+ * Runtime configuration to apply before running each test script
+ (e.g. enabling auto-vacuum, or disable lookaside).
+
+Running **all** tests is to run all tests in the full test set, plus a dozen
+or so permutations. The specific permutations that are run as part of "all"
+are defined in file *testrunner_data.tcl*.
+
+
+## 2.2. Commands to Run Tests
+
+To run the "veryquick" test set, use either of the following:
+
+```
+ ./testfixture $TESTDIR/testrunner.tcl
+ ./testfixture $TESTDIR/testrunner.tcl veryquick
+```
+
+To run the "full" test suite:
+
+```
+ ./testfixture $TESTDIR/testrunner.tcl full
+```
+
+To run the subset of the "full" test suite for which the test file name matches
+a specified pattern (e.g. all tests that start with "fts5"), either of:
+
+```
+ ./testfixture $TESTDIR/testrunner.tcl fts5%
+ ./testfixture $TESTDIR/testrunner.tcl 'fts5*'
+```
+
+Strictly speaking, for a test to be run the pattern must match the script
+filename, not including the directory, using the rules of Tcl's
+\[string match\] command. Except that before the matching is done, any "%"
+characters specified as part of the pattern are transformed to "\*".
+
+
+To run "all" tests (full + permutations):
+
+```
+ ./testfixture $TESTDIR/testrunner.tcl all
+```
+
+
+## 2.3. Investigating Binary Test Failures
+
+If a test fails, testrunner.tcl reports name of the Tcl test script and, if
+applicable, the name of the permutation, to stdout. This information can also
+be retrieved from either *testrunner.log* or *testrunner.db*.
+
+If there is no permutation, the individual test script may be run with:
+
+```
+ ./testfixture $PATH_TO_SCRIPT
+```
+
+Or, if the failure occured as part of a permutation:
+
+```
+ ./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
+```
+
+TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
+
+
+# 3. Source Code Tests
+
+The commands described in this section invoke the C compiler to build
+binaries from the source tree, then use those binaries to run Tcl and
+other tests. The advantages of this are that:
+
+ * it is possible to test multiple build configurations with a single
+ command, and
+
+ * it ensures that tests are always run using binaries created with the
+ same set of compiler options.
+
+The testrunner.tcl commands described in this section may be run using
+either a *testfixture* (or testfixture.exe) build, or with any other Tcl
+shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
+
+TODO: ./configure + Makefile.msc build systems.
+
+
+## 3.1. Commands to Run SQLite Tests
+
+The **mdevtest** command is equivalent to running the veryquick tests and
+the `make fuzztest` target once for each of two --enable-all builds - one
+with debugging enabled and one without:
+
+```
+ tclsh $TESTDIR/testrunner.tcl mdevtest
+```
+
+In other words, it is equivalent to running:
+
+```
+ $TOP/configure --enable-all --enable-debug
+ make fuzztest
+ make testfixture
+ ./testfixture $TOP/test/testrunner.tcl veryquick
+
+ # Then, after removing files created by the tests above:
+ $TOP/configure --enable-all OPTS="-O0"
+ make fuzztest
+ make testfixture
+ ./testfixture $TOP/test/testrunner.tcl veryquick
+```
+
+The **sdevtest** command is identical to the mdevtest command, except that the
+second of the two builds is a sanitizer build. Specifically, this means that
+OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
+
+```
+ tclsh $TESTDIR/testrunner.tcl sdevtest
+```
+
+The **release** command runs lots of tests under lots of builds. It runs
+different combinations of builds and tests depending on whether it is run
+on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
+of the specific tests run.
+
+```
+ tclsh $TESTDIR/testrunner.tcl release
+```
+
+As with source code tests, one or more patterns
+may be appended to any of the above commands (mdevtest, sdevtest or release).
+In that case only Tcl tests (no fuzz or other tests) that match the specified
+pattern are run. For example, to run the just the Tcl rtree tests in all
+builds and configurations supported by "release":
+
+```
+ tclsh $TESTDIR/testrunner.tcl release rtree%
+```
+
+
+## 3.2. Running ZipVFS Tests
+
+testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
+tests from the Zipvfs project with the following command:
+
+```
+ tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
+```
+
+This can be combined with any of "mdevtest", "sdevtest" or "release" to
+test both SQLite and Zipvfs with a single command:
+
+```
+ tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
+```
+
+
+## 3.3. Investigating Source Code Test Failures
+
+Investigating a test failure that occurs during source code testing is a
+two step process:
+
+ 1. Recreating the build configuration in which the test failed, and
+
+ 2. Re-running the actual test.
+
+To recreate a build configuration, use the testrunner.tcl **script** command
+to create a build script. A build script is a bash script on Linux or OSX, or
+a dos \*.bat file on windows. For example:
+
+```
+ # Create a script that recreates build configuration "Device-One" on
+ # Linux or OSX:
+ tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
+
+ # Create a script that recreates build configuration "Have-Not" on Windows:
+ tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
+```
+
+The generated bash or \*.bat file script accepts a single argument - a makefile
+target to build. This may be used either to run a `make` command test directly,
+or else to build a testfixture (or testfixture.exe) binary with which to
+run a Tcl test script, as described above.
+
+
+# 4. Extra testrunner.tcl Options
+
+The testrunner.tcl script options in this section may be used with both source
+code and binary tests.
+
+The **--buildonly** option instructs testrunner.tcl just to build the binaries
+required by a test, not to run any actual tests. For example:
+
+```
+ # Build binaries required by release test.
+ tclsh $TESTDIR/testrunner.tcl --buildonly release"
+```
+
+The **--dryrun** option prevents testrunner.tcl from building any binaries
+or running any tests. Instead, it just writes the shell commands that it
+would normally execute into the testrunner.log file. Example:
+
+```
+ # Log the shell commmands that make up the mdevtest test.
+ tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
+```
+
+The **--explain** option is similar to --dryrun in that it prevents testrunner.tcl
+from building any binaries or running any tests. The difference is that --explain
+prints on standard output a human-readable summary of all the builds and tests that
+would have been run.
+
+```
+ # Show what builds and tests would have been run
+ tclsh $TESTDIR/testrunner.tcl --explain mdevtest
+```
+
+
+# 5. Controlling CPU Core Utilization
+
+When running either binary or source code tests, testrunner.tcl reports the
+number of jobs it intends to use to stdout. e.g.
+
+```
+ $ ./testfixture $TESTDIR/testrunner.tcl
+ splitting work across 16 jobs
+ ... more output ...
+```
+
+By default, testfixture.tcl attempts to set the number of jobs to the number
+of real cores on the machine. This can be overridden using the "--jobs" (or -j)
+switch:
+
+```
+ $ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
+ splitting work across 8 jobs
+ ... more output ...
+```
+
+The number of jobs may also be changed while an instance of testrunner.tcl is
+running by exucuting the following command from the directory containing the
+testrunner.log and testrunner.db files:
+
+```
+ $ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
+```
diff --git a/sqlite/doc/trusted-schema.md b/sqlite/doc/trusted-schema.md
new file mode 100644
index 00000000..d431fd49
--- /dev/null
+++ b/sqlite/doc/trusted-schema.md
@@ -0,0 +1,142 @@
+# The new-security-options branch
+
+## The problem that the [new-security-options](/timeline?r=new-security-options) branch tries to solve
+
+An attacker might modify the schema of an SQLite database by adding
+structures that cause code to run when some other application opens and
+reads the database. For example, the attacker might replace a table
+definition with a view. Or the attacker might add triggers to tables
+or views, or add new CHECK constraints or generated columns or indexes
+with expressions in the index list or in the WHERE clause. If the
+added features invoke SQL functions or virtual tables with side effects,
+that might cause harm to the system if run by a high-privilege victim.
+Or, the added features might exfiltrate information if the database is
+read by a high-privilege victim.
+
+The changes in this branch strive to make it easier for high-privilege
+applications to safely read SQLite database files that might have been
+maliciously corrupted by an attacker.
+
+## Overview of changes in [new-security-options](/timeline?r=new-security-options)
+
+The basic idea is to tag every SQL function and virtual table with one
+of three risk levels:
+
+ 1. Innocuous
+ 2. Normal
+ 3. Direct-Only
+
+Innocuous functions/vtabs are safe and can be used at any time.
+Direct-only elements, in contrast, might have cause side-effects and
+should only be used from top-level SQL, not from within triggers or views nor
+in elements of the schema such as CHECK constraint, DEFAULT values,
+generated columns, index expressions, or in the WHERE clause of a
+partial index that are potentially under the control of an attacker.
+Normal elements behave like Innocuous if TRUSTED\_SCHEMA=on
+and behave like direct-only if TRUSTED\_SCHEMA=off.
+
+Application-defined functions and virtual tables go in as Normal unless
+the application takes deliberate steps to change the risk level.
+
+For backwards compatibility, the default is TRUSTED\_SCHEMA=on. Documentation
+will be updated to recommend applications turn TRUSTED\_SCHEMA to off.
+
+An innocuous function or virtual table is one that can only read content
+from the database file in which it resides, and can only alter the database
+in which it resides. Most SQL functions are innocuous. For example, there
+is no harm in an attacker running the abs() function.
+
+Direct-only elements that have side-effects that go outside the database file
+in which it lives, or return information from outside of the database file.
+Examples of direct-only elements include:
+
+ 1. The fts3\_tokenizer() function
+ 2. The writefile() function
+ 3. The readfile() function
+ 4. The zipvfs virtual table
+ 5. The csv virtual table
+
+We do not want an attacker to be able to add these kinds of things to
+the database schema and possibly trick a high-privilege application
+from performing any of these actions. Therefore, functions and vtabs
+with side-effects are marked as Direct-Only.
+
+Legacy applications might add other risky functions or vtabs. Those will
+go in as "Normal" by default. For optimal security, we want those risky
+app-defined functions and vtabs to be direct-only, but making that the
+default might break some legacy applications. Hence, all app-defined
+functions and vtabs go in as Normal, but the application can switch them
+over to "Direct-Only" behavior using a single pragma.
+
+The restrictions on the use of functions and virtual tables do not apply
+to TEMP. A TEMP VIEW or a TEMP TRIGGER can use any valid SQL function
+or virtual table. The idea is that TEMP views and triggers must be
+directly created by the application and are thus under the control of the
+application. TEMP views and triggers cannot be created by an attacker who
+corrupts the schema of a persistent database file. Hence TEMP views and
+triggers are safe.
+
+## Specific changes
+
+ 1. New sqlite3\_db\_config() option SQLITE\_DBCONFIG\_TRUSTED\_SCHEMA for
+ turning TRUSTED\_SCHEMA on and off. It defaults to ON.
+
+ 2. Compile-time option -DSQLITE\_TRUSTED\_SCHEMA=0 causes the default
+ TRUSTED\_SCHEMA setting to be off.
+
+ 3. New pragma "PRAGMA trusted\_schema=(ON\|OFF);". This provides access
+ to the TRUSTED_SCHEMA setting for application coded using scripting
+ languages or other secondary languages where they are unable to make
+ calls to sqlite3\_db\_config().
+
+ 4. New options for the "enc" parameter to sqlite3\_create\_function() and
+ its kin:
+
+
_SQLITE\_INNOCUOUS_ → tags the new functions as Innocuous
+
_SQLITE\_DIRECTONLY_ → tags the new functions as Direct-Only
+
+
+ 5. New options to sqlite3\_vtab\_config():
+
+
_SQLITE\_VTAB\_INNOCUOUS_ → tags the vtab as Innocuous
+
_SQLITE\_VTAB\_DIRECTONLY_ → tags the vtab as Direct-Only
+
+
+ 6. Change many of the functions and virtual tables in the SQLite source
+ tree to use one of the tags above.
+
+ 7. Enhanced PRAGMA function\_list and virtual-table "pragma\_function\_list"
+ with additional columns. The columns now are:
+
+
_name_ → Name of the function
+
_builtin_ → 1 for built-in functions. 0 otherwise.
+
_type_ → 's'=Scalar, 'a'=Aggregate, 'w'=Window
+
_enc_ → 'utf8', 'utf16le', or 'utf16be'
+
_narg_ → number of argument
+
_flags_ → Bitmask of SQLITE\_INNOCUOUS, SQLITE\_DIRECTONLY,
+ SQLITE\_DETERMINISTIC, SQLITE\_SUBTYPE, and
+ SQLITE\_FUNC\_INTERNAL flags.
+
+
The last four columns are new.
+
+ 8. The function\_list PRAGMA now also shows all entries for each function.
+ So, for example, if a function can take either 2 or 3 arguments,
+ there are separate rows for the 2-argument and 3-argument versions of
+ the function.
+
+## Additional Notes
+
+The function_list enhancements allow the application to query the set
+of SQL functions that meet various criteria. For example, to see all
+SQL functions that are never allowed to be used in the schema or in
+trigger or views:
+
+~~~
+ SELECT DISTINCT name FROM pragma_function_list
+ WHERE (flags & 0x80000)!=0
+ ORDER BY name;
+~~~
+
+Doing the same is not possible for virtual tables, as a virtual table
+might be Innocuous, Normal, or Direct-Only depending on the arguments
+passed into the xConnect method.
diff --git a/sqlite/doc/vdbesort-memory.md b/sqlite/doc/vdbesort-memory.md
new file mode 100644
index 00000000..5c3dd62d
--- /dev/null
+++ b/sqlite/doc/vdbesort-memory.md
@@ -0,0 +1,49 @@
+
+20-11-2020
+
+# Memory Allocation In vdbesort.c
+
+Memory allocation is slightly different depending on:
+
+ * whether or not SQLITE_CONFIG_SMALL_MALLOC is set, and
+ * whether or not worker threads are enabled.
+
+## SQLITE_CONFIG_SMALL_MALLOC=0
+
+Assuming SQLITE_CONFIG_SMALL_MALLOC is not set, keys passed to the sorter are
+added to an in-memory buffer. This buffer is grown using sqlite3Realloc() as
+required it reaches the size configured for the main pager cache using "PRAGMA
+cache_size". i.e. if the user has executed "PRAGMA main.cache_size = -2048",
+then this buffer is allowed to grow up to 2MB in size.
+
+Once the buffer has grown to its threshold, keys are sorted and written to
+a temp file. If worker threads are not enabled, this is the only significant
+allocation the sorter module makes. After keys are sorted and flushed out to
+the temp file, the buffer is reused to accumulate the next batch of keys.
+
+If worker threads are available, then the buffer is passed to a worker thread
+to sort and flush once it is full, and a new buffer allocated to allow the
+main thread to continue to accumulate keys. Buffers are reused once they
+have been flushed, so in this case at most (nWorker+1) buffers are allocated
+and used, where nWorker is the number of configured worker threads.
+
+There are no other significant users of heap memory in the sorter module.
+Once sorted buffers of keys have been flushed to disk, they are read back
+either by mapping the file (via sqlite3_file.xFetch()) or else read back
+in one page at a time.
+
+All buffers are allocated by the main thread. A sorter object is associated
+with a single database connection, to which it holds a pointer.
+
+## SQLITE_CONFIG_SMALL_MALLOC=1
+
+This case is similar to the above, except that instead of accumulating
+multiple keys in a single large buffer, sqlite3VdbeSorterWrite() stores
+keys in a regular heap-memory linked list (one allocation per element).
+List elements are freed as they are flushed to disk, either by the main
+thread or by a worker thread.
+
+Each time a key is added the sorter (and an allocation made),
+sqlite3HeapNearlyFull() is called. If it returns true, the current
+list of keys is flushed to a temporary file, even if it has not yet
+reached the size threshold.
diff --git a/sqlite/doc/wal-lock.md b/sqlite/doc/wal-lock.md
new file mode 100644
index 00000000..d74bb88b
--- /dev/null
+++ b/sqlite/doc/wal-lock.md
@@ -0,0 +1,88 @@
+# Wal-Mode Blocking Locks
+
+On some Unix-like systems, SQLite may be configured to use POSIX blocking locks
+by:
+
+ * building the library with SQLITE\_ENABLE\_SETLK\_TIMEOUT defined, and
+ * configuring a timeout in ms using the sqlite3\_busy\_timeout() API.
+
+Blocking locks may be advantageous as (a) waiting database clients do not
+need to continuously poll the database lock, and (b) using blocking locks
+facilitates transfer of OS priority between processes when a high priority
+process is blocked by a lower priority one.
+
+Only read/write clients use blocking locks. Clients that have read-only access
+to the \*-shm file nevery use blocking locks.
+
+Threads or processes that access a single database at a time never deadlock as
+a result of blocking database locks. But it is of course possible for threads
+that lock multiple databases simultaneously to do so. In most cases the OS will
+detect the deadlock and return an error.
+
+## Wal Recovery
+
+Wal database "recovery" is a process required when the number of connected
+database clients changes from zero to one. In this case, a client is
+considered to connect to the database when it first reads data from it.
+Before recovery commences, an exclusive WRITER lock is taken.
+
+Without blocking locks, if two clients attempt recovery simultaneously, one
+fails to obtain the WRITER lock and either invokes the busy-handler callback or
+returns SQLITE\_BUSY to the user. With blocking locks configured, the second
+client blocks on the WRITER lock.
+
+## Database Readers
+
+Usually, read-only are not blocked by any other database clients, so they
+have no need of blocking locks.
+
+If a read-only transaction is being opened on a snapshot, the CHECKPOINTER
+lock is required briefly as part of opening the transaction (to check that a
+checkpointer is not currently overwriting the snapshot being opened). A
+blocking lock is used to obtain the CHECKPOINTER lock in this case. A snapshot
+opener may therefore block on and transfer priority to a checkpointer in some
+cases.
+
+## Database Writers
+
+A database writer must obtain the exclusive WRITER lock. It uses a blocking
+lock to do so if any of the following are true:
+
+ * the transaction is an implicit one consisting of a single DML or DDL
+ statement, or
+ * the transaction is opened using BEGIN IMMEDIATE or BEGIN EXCLUSIVE, or
+ * the first SQL statement executed following the BEGIN command is a DML or
+ DDL statement (not a read-only statement like a SELECT).
+
+In other words, in all cases except when an open read-transaction is upgraded
+to a write-transaction. In that case a non-blocking lock is used.
+
+## Database Checkpointers
+
+Database checkpointers takes the following locks, in order:
+
+ * The exclusive CHECKPOINTER lock.
+ * The exclusive WRITER lock (FULL, RESTART and TRUNCATE only).
+ * Exclusive lock on read-mark slots 1-N. These are immediately released after being taken.
+ * Exclusive lock on read-mark 0.
+ * Exclusive lock on read-mark slots 1-N again. These are immediately released
+ after being taken (RESTART and TRUNCATE only).
+
+All of the above use blocking locks.
+
+## Summary
+
+With blocking locks configured, the only cases in which clients should see an
+SQLITE\_BUSY error are:
+
+ * if the OS does not grant a blocking lock before the configured timeout
+ expires, and
+ * when an open read-transaction is upgraded to a write-transaction.
+
+In all other cases the blocking locks implementation should prevent clients
+from having to handle SQLITE\_BUSY errors and facilitate appropriate transfer
+of priorities between competing clients.
+
+Clients that lock multiple databases simultaneously must be wary of deadlock.
+
+
diff --git a/sqlite/ext/README.md b/sqlite/ext/README.md
new file mode 100644
index 00000000..933a33d0
--- /dev/null
+++ b/sqlite/ext/README.md
@@ -0,0 +1,8 @@
+## Loadable Extensions
+
+Various [loadable extensions](https://www.sqlite.org/loadext.html) for
+SQLite are found in subfolders.
+
+Most subfolders are dedicated to a single loadable extension (for
+example FTS5, or RTREE). But the misc/ subfolder contains a collection
+of smaller single-file extensions.
diff --git a/sqlite/ext/README.txt b/sqlite/ext/README.txt
deleted file mode 100644
index 009495f5..00000000
--- a/sqlite/ext/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Version loadable extensions to SQLite are found in subfolders
-of this folder.
diff --git a/sqlite/ext/async/sqlite3async.c b/sqlite/ext/async/sqlite3async.c
index b6f4a4bd..eed7c8d7 100644
--- a/sqlite/ext/async/sqlite3async.c
+++ b/sqlite/ext/async/sqlite3async.c
@@ -1704,4 +1704,3 @@ int sqlite3async_control(int op, ...){
}
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ASYNCIO) */
-
diff --git a/sqlite/ext/async/sqlite3async.h b/sqlite/ext/async/sqlite3async.h
index 5b20d718..13b23bc6 100644
--- a/sqlite/ext/async/sqlite3async.h
+++ b/sqlite/ext/async/sqlite3async.h
@@ -220,4 +220,3 @@ int sqlite3async_control(int op, ...);
} /* End of the 'extern "C"' block */
#endif
#endif /* ifndef __SQLITEASYNC_H_ */
-
diff --git a/sqlite/ext/consio/console_io.c b/sqlite/ext/consio/console_io.c
new file mode 100755
index 00000000..75324a34
--- /dev/null
+++ b/sqlite/ext/consio/console_io.c
@@ -0,0 +1,773 @@
+/*
+** 2023 November 4
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+********************************************************************************
+** This file implements various interfaces used for console and stream I/O
+** by the SQLite project command-line tools, as explained in console_io.h .
+** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there.
+*/
+
+#ifndef SQLITE_CDECL
+# define SQLITE_CDECL
+#endif
+
+#ifndef SHELL_NO_SYSINC
+# include
+# include
+# include
+# include
+# include
+# include "sqlite3.h"
+#endif
+#ifndef HAVE_CONSOLE_IO_H
+# include "console_io.h"
+#endif
+#if defined(_MSC_VER)
+# pragma warning(disable : 4204)
+#endif
+
+#ifndef SQLITE_CIO_NO_TRANSLATE
+# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
+# ifndef SHELL_NO_SYSINC
+# include
+# include
+# undef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# include
+# endif
+# define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */
+# else
+# ifndef SHELL_NO_SYSINC
+# include
+# endif
+# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */
+# endif
+#else
+# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
+#endif
+
+#if CIO_WIN_WC_XLATE
+static HANDLE handleOfFile(FILE *pf){
+ int fileDesc = _fileno(pf);
+ union { intptr_t osfh; HANDLE fh; } fid = {
+ (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE
+ };
+ return fid.fh;
+}
+#endif
+
+#ifndef SQLITE_CIO_NO_TRANSLATE
+typedef struct PerStreamTags {
+# if CIO_WIN_WC_XLATE
+ HANDLE hx;
+ DWORD consMode;
+ char acIncomplete[4];
+# else
+ short reachesConsole;
+# endif
+ FILE *pf;
+} PerStreamTags;
+
+/* Define NULL-like value for things which can validly be 0. */
+# define SHELL_INVALID_FILE_PTR ((FILE *)~0)
+# if CIO_WIN_WC_XLATE
+# define SHELL_INVALID_CONS_MODE 0xFFFF0000
+# endif
+
+# if CIO_WIN_WC_XLATE
+# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \
+ {0,0,0,0}, SHELL_INVALID_FILE_PTR }
+# else
+# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR }
+# endif
+
+/* Quickly say whether a known output is going to the console. */
+# if CIO_WIN_WC_XLATE
+static short pstReachesConsole(PerStreamTags *ppst){
+ return (ppst->hx != INVALID_HANDLE_VALUE);
+}
+# else
+# define pstReachesConsole(ppst) 0
+# endif
+
+# if CIO_WIN_WC_XLATE
+static void restoreConsoleArb(PerStreamTags *ppst){
+ if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode);
+}
+# else
+# define restoreConsoleArb(ppst)
+# endif
+
+/* Say whether FILE* appears to be a console, collect associated info. */
+static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){
+# if CIO_WIN_WC_XLATE
+ short rv = 0;
+ DWORD dwCM = SHELL_INVALID_CONS_MODE;
+ HANDLE fh = handleOfFile(pf);
+ ppst->pf = pf;
+ if( INVALID_HANDLE_VALUE != fh ){
+ rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM));
+ }
+ ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE;
+ ppst->consMode = dwCM;
+ return rv;
+# else
+ ppst->pf = pf;
+ ppst->reachesConsole = ( (short)isatty(fileno(pf)) );
+ return ppst->reachesConsole;
+# endif
+}
+
+# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+# define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4)
+# endif
+
+# if CIO_WIN_WC_XLATE
+/* Define console modes for use with the Windows Console API. */
+# define SHELL_CONI_MODE \
+ (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \
+ | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT)
+# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \
+ | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+# endif
+
+typedef struct ConsoleInfo {
+ PerStreamTags pstSetup[3];
+ PerStreamTags pstDesignated[3];
+ StreamsAreConsole sacSetup;
+} ConsoleInfo;
+
+static short isValidStreamInfo(PerStreamTags *ppst){
+ return (ppst->pf != SHELL_INVALID_FILE_PTR);
+}
+
+static ConsoleInfo consoleInfo = {
+ { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER },
+ { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER },
+ SAC_NoConsole /* sacSetup */
+};
+
+SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0;
+
+# if CIO_WIN_WC_XLATE
+static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){
+ if( pstReachesConsole(ppst) ){
+ DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE;
+ SetConsoleMode(ppst->hx, cm);
+ }
+}
+# else
+# define maybeSetupAsConsole(ppst,odir)
+# endif
+
+SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){
+# if CIO_WIN_WC_XLATE
+ int ix = 0;
+ while( ix < 6 ){
+ PerStreamTags *ppst = (ix<3)?
+ &consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3];
+ maybeSetupAsConsole(ppst, (ix % 3)>0);
+ ++ix;
+ }
+# endif
+}
+
+SQLITE_INTERNAL_LINKAGE StreamsAreConsole
+consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){
+ StreamsAreConsole rv = SAC_NoConsole;
+ FILE* apf[3] = { pfIn, pfOut, pfErr };
+ int ix;
+ for( ix = 2; ix >= 0; --ix ){
+ PerStreamTags *ppst = &consoleInfo.pstSetup[ix];
+ if( streamOfConsole(apf[ix], ppst) ){
+ rv |= (SAC_InConsole< 0 ) fflush(apf[ix]);
+ }
+ consoleInfo.sacSetup = rv;
+ consoleRenewSetup();
+ return rv;
+}
+
+SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){
+# if CIO_WIN_WC_XLATE
+ static ConsoleInfo *pci = &consoleInfo;
+ if( pci->sacSetup ){
+ int ix;
+ for( ix=0; ix<3; ++ix ){
+ if( pci->sacSetup & (SAC_InConsole<pstSetup[ix];
+ SetConsoleMode(ppst->hx, ppst->consMode);
+ }
+ }
+ }
+# endif
+}
+#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
+
+#ifdef SQLITE_CIO_INPUT_REDIR
+/* Say whether given FILE* is among those known, via either
+** consoleClassifySetup() or set{Output,Error}Stream, as
+** readable, and return an associated PerStreamTags pointer
+** if so. Otherwise, return 0.
+*/
+static PerStreamTags * isKnownReadable(FILE *pf){
+ static PerStreamTags *apst[] = {
+ &consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0
+ };
+ int ix = 0;
+ do {
+ if( apst[ix]->pf == pf ) break;
+ } while( apst[++ix] != 0 );
+ return apst[ix];
+}
+#endif
+
+#ifndef SQLITE_CIO_NO_TRANSLATE
+/* Say whether given FILE* is among those known, via either
+** consoleClassifySetup() or set{Output,Error}Stream, as
+** writable, and return an associated PerStreamTags pointer
+** if so. Otherwise, return 0.
+*/
+static PerStreamTags * isKnownWritable(FILE *pf){
+ static PerStreamTags *apst[] = {
+ &consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2],
+ &consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0
+ };
+ int ix = 0;
+ do {
+ if( apst[ix]->pf == pf ) break;
+ } while( apst[++ix] != 0 );
+ return apst[ix];
+}
+
+static FILE *designateEmitStream(FILE *pf, unsigned chix){
+ FILE *rv = consoleInfo.pstDesignated[chix].pf;
+ if( pf == invalidFileStream ) return rv;
+ else{
+ /* Setting a possibly new output stream. */
+ PerStreamTags *ppst = isKnownWritable(pf);
+ if( ppst != 0 ){
+ PerStreamTags pst = *ppst;
+ consoleInfo.pstDesignated[chix] = pst;
+ }else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]);
+ }
+ return rv;
+}
+
+SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){
+ return designateEmitStream(pf, 1);
+}
+# ifdef CONSIO_SET_ERROR_STREAM
+SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){
+ return designateEmitStream(pf, 2);
+}
+# endif
+#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
+
+#ifndef SQLITE_CIO_NO_SETMODE
+# if CIO_WIN_WC_XLATE
+static void setModeFlushQ(FILE *pf, short bFlush, int mode){
+ if( bFlush ) fflush(pf);
+ _setmode(_fileno(pf), mode);
+}
+# else
+# define setModeFlushQ(f, b, m) if(b) fflush(f)
+# endif
+
+SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){
+ setModeFlushQ(pf, bFlush, _O_BINARY);
+}
+SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){
+ setModeFlushQ(pf, bFlush, _O_TEXT);
+}
+# undef setModeFlushQ
+
+#else /* defined(SQLITE_CIO_NO_SETMODE) */
+# define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0)
+# define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0)
+#endif /* defined(SQLITE_CIO_NO_SETMODE) */
+
+#ifndef SQLITE_CIO_NO_TRANSLATE
+# if CIO_WIN_WC_XLATE
+/* Write buffer cBuf as output to stream known to reach console,
+** limited to ncTake char's. Return ncTake on success, else 0. */
+static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){
+ int rv = 0;
+ if( z!=NULL ){
+ int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0);
+ if( nwc > 0 ){
+ WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR));
+ if( zw!=NULL ){
+ nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc);
+ if( nwc > 0 ){
+ /* Translation from UTF-8 to UTF-16, then WCHARs out. */
+ if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){
+ rv = ncTake;
+ }
+ }
+ sqlite3_free(zw);
+ }
+ }
+ }
+ return rv;
+}
+
+/* For {f,o,e}PrintfUtf8() when stream is known to reach console. */
+static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){
+ char *z = sqlite3_vmprintf(zFormat, ap);
+ if( z ){
+ int rv = conZstrEmit(ppst, z, (int)strlen(z));
+ sqlite3_free(z);
+ return rv;
+ }else return 0;
+}
+# endif /* CIO_WIN_WC_XLATE */
+
+# ifdef CONSIO_GET_EMIT_STREAM
+static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix,
+ PerStreamTags *ppst){
+ PerStreamTags *rv = isKnownWritable(pf);
+ short isValid = (rv!=0)? isValidStreamInfo(rv) : 0;
+ if( rv != 0 && isValid ) return rv;
+ streamOfConsole(pf, ppst);
+ return ppst;
+}
+# endif
+
+/* Get stream info, either for designated output or error stream when
+** chix equals 1 or 2, or for an arbitrary stream when chix == 0.
+** In either case, ppst references a caller-owned PerStreamTags
+** struct which may be filled in if none of the known writable
+** streams is being held by consoleInfo. The ppf parameter is a
+** byref output when chix!=0 and a byref input when chix==0.
+ */
+static PerStreamTags *
+getEmitStreamInfo(unsigned chix, PerStreamTags *ppst,
+ /* in/out */ FILE **ppf){
+ PerStreamTags *ppstTry;
+ FILE *pfEmit;
+ if( chix > 0 ){
+ ppstTry = &consoleInfo.pstDesignated[chix];
+ if( !isValidStreamInfo(ppstTry) ){
+ ppstTry = &consoleInfo.pstSetup[chix];
+ pfEmit = ppst->pf;
+ }else pfEmit = ppstTry->pf;
+ if( !isValidStreamInfo(ppstTry) ){
+ pfEmit = (chix > 1)? stderr : stdout;
+ ppstTry = ppst;
+ streamOfConsole(pfEmit, ppstTry);
+ }
+ *ppf = pfEmit;
+ }else{
+ ppstTry = isKnownWritable(*ppf);
+ if( ppstTry != 0 ) return ppstTry;
+ streamOfConsole(*ppf, ppst);
+ return ppst;
+ }
+ return ppstTry;
+}
+
+SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){
+ va_list ap;
+ int rv;
+ FILE *pfOut;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
+# else
+ getEmitStreamInfo(1, &pst, &pfOut);
+# endif
+ assert(zFormat!=0);
+ va_start(ap, zFormat);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ){
+ rv = conioVmPrintf(ppst, zFormat, ap);
+ }else{
+# endif
+ rv = vfprintf(pfOut, zFormat, ap);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+ va_end(ap);
+ return rv;
+}
+
+SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){
+ va_list ap;
+ int rv;
+ FILE *pfErr;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
+# else
+ getEmitStreamInfo(2, &pst, &pfErr);
+# endif
+ assert(zFormat!=0);
+ va_start(ap, zFormat);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ){
+ rv = conioVmPrintf(ppst, zFormat, ap);
+ }else{
+# endif
+ rv = vfprintf(pfErr, zFormat, ap);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+ va_end(ap);
+ return rv;
+}
+
+SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){
+ va_list ap;
+ int rv;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
+# else
+ getEmitStreamInfo(0, &pst, &pfO);
+# endif
+ assert(zFormat!=0);
+ va_start(ap, zFormat);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ){
+ maybeSetupAsConsole(ppst, 1);
+ rv = conioVmPrintf(ppst, zFormat, ap);
+ if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
+ }else{
+# endif
+ rv = vfprintf(pfO, zFormat, ap);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+ va_end(ap);
+ return rv;
+}
+
+SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
+# else
+ getEmitStreamInfo(0, &pst, &pfO);
+# endif
+ assert(z!=0);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ){
+ int rv;
+ maybeSetupAsConsole(ppst, 1);
+ rv = conZstrEmit(ppst, z, (int)strlen(z));
+ if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
+ return rv;
+ }else {
+# endif
+ return (fputs(z, pfO)<0)? 0 : (int)strlen(z);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+
+SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){
+ FILE *pfErr;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
+# else
+ getEmitStreamInfo(2, &pst, &pfErr);
+# endif
+ assert(z!=0);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z));
+ else {
+# endif
+ return (fputs(z, pfErr)<0)? 0 : (int)strlen(z);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+
+SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){
+ FILE *pfOut;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
+# else
+ getEmitStreamInfo(1, &pst, &pfOut);
+# endif
+ assert(z!=0);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z));
+ else {
+# endif
+ return (fputs(z, pfOut)<0)? 0 : (int)strlen(z);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+
+#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
+
+#if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE))
+/* Skip over as much z[] input char sequence as is valid UTF-8,
+** limited per nAccept char's or whole characters and containing
+** no char cn such that ((1<=0 => char count, nAccept<0 => character
+ */
+SQLITE_INTERNAL_LINKAGE const char*
+zSkipValidUtf8(const char *z, int nAccept, long ccm){
+ int ng = (nAccept<0)? -nAccept : 0;
+ const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
+ assert(z!=0);
+ while( (pcLimit)? (z= pcLimit ) return z;
+ else{
+ char ct = *zt++;
+ if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){
+ /* Trailing bytes are too few, too many, or invalid. */
+ return z;
+ }
+ }
+ } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */
+ z = zt;
+ }
+ }
+ return z;
+}
+#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/
+
+#ifndef SQLITE_CIO_NO_TRANSLATE
+# ifdef CONSIO_SPUTB
+SQLITE_INTERNAL_LINKAGE int
+fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){
+ assert(pfO!=0);
+# if CIO_WIN_WC_XLATE
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+ PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
+ if( pstReachesConsole(ppst) ){
+ int rv;
+ maybeSetupAsConsole(ppst, 1);
+ rv = conZstrEmit(ppst, cBuf, nAccept);
+ if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
+ return rv;
+ }else {
+# endif
+ return (int)fwrite(cBuf, 1, nAccept, pfO);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+# endif
+
+SQLITE_INTERNAL_LINKAGE int
+oPutbUtf8(const char *cBuf, int nAccept){
+ FILE *pfOut;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+# if CIO_WIN_WC_XLATE
+ PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
+# else
+ getEmitStreamInfo(1, &pst, &pfOut);
+# endif
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ){
+ return conZstrEmit(ppst, cBuf, nAccept);
+ }else {
+# endif
+ return (int)fwrite(cBuf, 1, nAccept, pfOut);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+
+/*
+** Flush the given output stream. Return non-zero for success, else 0.
+*/
+#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
+SQLITE_INTERNAL_LINKAGE int
+fFlushBuffer(FILE *pfOut){
+# if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE)
+ return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0;
+# else
+ return fflush(pfOut);
+# endif
+}
+#endif
+
+#if CIO_WIN_WC_XLATE \
+ && !defined(SHELL_OMIT_FIO_DUPE) \
+ && defined(SQLITE_USE_ONLY_WIN32)
+static struct FileAltIds {
+ int fd;
+ HANDLE fh;
+} altIdsOfFile(FILE *pf){
+ struct FileAltIds rv = { _fileno(pf) };
+ union { intptr_t osfh; HANDLE fh; } fid = {
+ (rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE
+ };
+ rv.fh = fid.fh;
+ return rv;
+}
+
+SQLITE_INTERNAL_LINKAGE size_t
+cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){
+ size_t rv = 0;
+ struct FileAltIds fai = altIdsOfFile(pf);
+ int fmode = _setmode(fai.fd, _O_BINARY);
+ _setmode(fai.fd, fmode);
+ while( rv < ocnt ){
+ size_t nbo = osz;
+ while( nbo > 0 ){
+ DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo;
+ BOOL wrc = TRUE;
+ BOOL genCR = (fmode & _O_TEXT)!=0;
+ if( genCR ){
+ const char *pnl = (const char*)memchr(buf, '\n', nbo);
+ if( pnl ) nbo = pnl - (const char*)buf;
+ else genCR = 0;
+ }
+ if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0);
+ if( genCR && wrc ){
+ wrc = WriteFile(fai.fh, "\r\n", 2, 0,0);
+ ++dwno; /* Skip over the LF */
+ }
+ if( !wrc ) return rv;
+ buf = (const char*)buf + dwno;
+ nbo += dwno;
+ }
+ ++rv;
+ }
+ return rv;
+}
+
+/* An fgets() equivalent, using Win32 file API for actual input.
+** Input ends when given buffer is filled or a newline is read.
+** If the FILE object is in text mode, swallows 0x0D. (ASCII CR)
+*/
+SQLITE_INTERNAL_LINKAGE char *
+cfGets(char *cBuf, int n, FILE *pf){
+ int nci = 0;
+ struct FileAltIds fai = altIdsOfFile(pf);
+ int fmode = _setmode(fai.fd, _O_BINARY);
+ BOOL eatCR = (fmode & _O_TEXT)!=0;
+ _setmode(fai.fd, fmode);
+ while( nci < n-1 ){
+ DWORD nr;
+ if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break;
+ if( nr>0 && (!eatCR || cBuf[nci]!='\r') ){
+ nci += nr;
+ if( cBuf[nci-nr]=='\n' ) break;
+ }
+ }
+ if( nci < n ) cBuf[nci] = 0;
+ return (nci>0)? cBuf : 0;
+}
+# else
+# define cfWrite(b,os,no,f) fwrite(b,os,no,f)
+# define cfGets(b,n,f) fgets(b,n,f)
+# endif
+
+# ifdef CONSIO_EPUTB
+SQLITE_INTERNAL_LINKAGE int
+ePutbUtf8(const char *cBuf, int nAccept){
+ FILE *pfErr;
+ PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
+ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
+# if CIO_WIN_WC_XLATE
+ if( pstReachesConsole(ppst) ){
+ return conZstrEmit(ppst, cBuf, nAccept);
+ }else {
+# endif
+ return (int)cfWrite(cBuf, 1, nAccept, pfErr);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+# endif /* defined(CONSIO_EPUTB) */
+
+SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
+ if( pfIn==0 ) pfIn = stdin;
+# if CIO_WIN_WC_XLATE
+ if( pfIn == consoleInfo.pstSetup[0].pf
+ && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){
+# if CIO_WIN_WC_XLATE==1
+# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */
+ WCHAR wcBuf[SHELL_GULP+1];
+ int lend = 0, noc = 0;
+ if( ncMax > 0 ) cBuf[0] = 0;
+ while( noc < ncMax-8-1 && !lend ){
+ /* There is room for at least 2 more characters and a 0-terminator. */
+ int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4;
+# undef SHELL_GULP
+ DWORD nbr = 0;
+ BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0);
+ if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){
+ /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */
+ DWORD nbrx;
+ bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0);
+ if( bRC ) nbr += nbrx;
+ }
+ if( !bRC || (noc==0 && nbr==0) ) return 0;
+ if( nbr > 0 ){
+ int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0);
+ if( nmb != 0 && noc+nmb <= ncMax ){
+ int iseg = noc;
+ nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0);
+ noc += nmb;
+ /* Fixup line-ends as coded by Windows for CR (or "Enter".)
+ ** This is done without regard for any setMode{Text,Binary}()
+ ** call that might have been done on the interactive input.
+ */
+ if( noc > 0 ){
+ if( cBuf[noc-1]=='\n' ){
+ lend = 1;
+ if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n';
+ }
+ }
+ /* Check for ^Z (anywhere in line) too, to act as EOF. */
+ while( iseg < noc ){
+ if( cBuf[iseg]=='\x1a' ){
+ noc = iseg; /* Chop ^Z and anything following. */
+ lend = 1; /* Counts as end of line too. */
+ break;
+ }
+ ++iseg;
+ }
+ }else break; /* Drop apparent garbage in. (Could assert.) */
+ }else break;
+ }
+ /* If got nothing, (after ^Z chop), must be at end-of-file. */
+ if( noc > 0 ){
+ cBuf[noc] = 0;
+ return cBuf;
+ }else return 0;
+# endif
+ }else{
+# endif
+ return cfGets(cBuf, ncMax, pfIn);
+# if CIO_WIN_WC_XLATE
+ }
+# endif
+}
+#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
+
+#if defined(_MSC_VER)
+# pragma warning(default : 4204)
+#endif
+
+#undef SHELL_INVALID_FILE_PTR
diff --git a/sqlite/ext/consio/console_io.h b/sqlite/ext/consio/console_io.h
new file mode 100644
index 00000000..1affa15b
--- /dev/null
+++ b/sqlite/ext/consio/console_io.h
@@ -0,0 +1,287 @@
+/*
+** 2023 November 1
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+********************************************************************************
+** This file exposes various interfaces used for console and other I/O
+** by the SQLite project command-line tools. These interfaces are used
+** at either source conglomeration time, compilation time, or run time.
+** This source provides for either inclusion into conglomerated,
+** "single-source" forms or separate compilation then linking.
+**
+** Platform dependencies are "hidden" here by various stratagems so
+** that, provided certain conditions are met, the programs using this
+** source or object code compiled from it need no explicit conditional
+** compilation in their source for their console and stream I/O.
+**
+** The symbols and functionality exposed here are not a public API.
+** This code may change in tandem with other project code as needed.
+**
+** When this .h file and its companion .c are directly incorporated into
+** a source conglomeration (such as shell.c), the preprocessor symbol
+** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O
+** translation for Windows is effected for the build.
+*/
+#define HAVE_CONSOLE_IO_H 1
+#ifndef SQLITE_INTERNAL_LINKAGE
+# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */
+# include
+#else
+# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */
+#endif
+
+#ifndef SQLITE3_H
+# include "sqlite3.h"
+#endif
+
+#ifndef SQLITE_CIO_NO_CLASSIFY
+
+/* Define enum for use with following function. */
+typedef enum StreamsAreConsole {
+ SAC_NoConsole = 0,
+ SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4,
+ SAC_AnyConsole = 0x7
+} StreamsAreConsole;
+
+/*
+** Classify the three standard I/O streams according to whether
+** they are connected to a console attached to the process.
+**
+** Returns the bit-wise OR of SAC_{In,Out,Err}Console values,
+** or SAC_NoConsole if none of the streams reaches a console.
+**
+** This function should be called before any I/O is done with
+** the given streams. As a side-effect, the given inputs are
+** recorded so that later I/O operations on them may be done
+** differently than the C library FILE* I/O would be done,
+** iff the stream is used for the I/O functions that follow,
+** and to support the ones that use an implicit stream.
+**
+** On some platforms, stream or console mode alteration (aka
+** "Setup") may be made which is undone by consoleRestore().
+*/
+SQLITE_INTERNAL_LINKAGE StreamsAreConsole
+consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr );
+/* A usual call for convenience: */
+#define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr)
+
+/*
+** After an initial call to consoleClassifySetup(...), renew
+** the same setup it effected. (A call not after is an error.)
+** This will restore state altered by consoleRestore();
+**
+** Applications which run an inferior (child) process which
+** inherits the same I/O streams may call this function after
+** such a process exits to guard against console mode changes.
+*/
+SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void);
+
+/*
+** Undo any side-effects left by consoleClassifySetup(...).
+**
+** This should be called after consoleClassifySetup() and
+** before the process terminates normally. It is suitable
+** for use with the atexit() C library procedure. After
+** this call, no console I/O should be done until one of
+** console{Classify or Renew}Setup(...) is called again.
+**
+** Applications which run an inferior (child) process that
+** inherits the same I/O streams might call this procedure
+** before so that said process will have a console setup
+** however users have configured it or come to expect.
+*/
+SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void );
+
+#else /* defined(SQLITE_CIO_NO_CLASSIFY) */
+# define consoleClassifySetup(i,o,e)
+# define consoleRenewSetup()
+# define consoleRestore()
+#endif /* defined(SQLITE_CIO_NO_CLASSIFY) */
+
+#ifndef SQLITE_CIO_NO_REDIRECT
+/*
+** Set stream to be used for the functions below which write
+** to "the designated X stream", where X is Output or Error.
+** Returns the previous value.
+**
+** Alternatively, pass the special value, invalidFileStream,
+** to get the designated stream value without setting it.
+**
+** Before the designated streams are set, they default to
+** those passed to consoleClassifySetup(...), and before
+** that is called they default to stdout and stderr.
+**
+** It is error to close a stream so designated, then, without
+** designating another, use the corresponding {o,e}Emit(...).
+*/
+SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream;
+SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf);
+# ifdef CONSIO_SET_ERROR_STREAM
+SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf);
+# endif
+#else
+# define setOutputStream(pf)
+# define setErrorStream(pf)
+#endif /* !defined(SQLITE_CIO_NO_REDIRECT) */
+
+#ifndef SQLITE_CIO_NO_TRANSLATE
+/*
+** Emit output like fprintf(). If the output is going to the
+** console and translation from UTF-8 is necessary, perform
+** the needed translation. Otherwise, write formatted output
+** to the provided stream almost as-is, possibly with newline
+** translation as specified by set{Binary,Text}Mode().
+*/
+SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...);
+/* Like fPrintfUtf8 except stream is always the designated output. */
+SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...);
+/* Like fPrintfUtf8 except stream is always the designated error. */
+SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...);
+
+/*
+** Emit output like fputs(). If the output is going to the
+** console and translation from UTF-8 is necessary, perform
+** the needed translation. Otherwise, write given text to the
+** provided stream almost as-is, possibly with newline
+** translation as specified by set{Binary,Text}Mode().
+*/
+SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO);
+/* Like fPutsUtf8 except stream is always the designated output. */
+SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z);
+/* Like fPutsUtf8 except stream is always the designated error. */
+SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z);
+
+/*
+** Emit output like fPutsUtf8(), except that the length of the
+** accepted char or character sequence is limited by nAccept.
+**
+** Returns the number of accepted char values.
+*/
+#ifdef CONSIO_SPUTB
+SQLITE_INTERNAL_LINKAGE int
+fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept);
+/* Like fPutbUtf8 except stream is always the designated output. */
+#endif
+SQLITE_INTERNAL_LINKAGE int
+oPutbUtf8(const char *cBuf, int nAccept);
+/* Like fPutbUtf8 except stream is always the designated error. */
+#ifdef CONSIO_EPUTB
+SQLITE_INTERNAL_LINKAGE int
+ePutbUtf8(const char *cBuf, int nAccept);
+#endif
+
+/*
+** Flush the given output stream. Return non-zero for success, else 0.
+*/
+#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
+SQLITE_INTERNAL_LINKAGE int
+fFlushBuffer(FILE *pfOut);
+#endif
+
+/*
+** Collect input like fgets(...) with special provisions for input
+** from the console on such platforms as require same. Newline
+** translation may be done as set by set{Binary,Text}Mode().
+** As a convenience, pfIn==NULL is treated as stdin.
+*/
+SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
+/* Like fGetsUtf8 except stream is always the designated input. */
+/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */
+
+#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
+
+#ifndef SQLITE_CIO_NO_SETMODE
+/*
+** Set given stream for binary mode, where newline translation is
+** not done, or for text mode where, for some platforms, newlines
+** are translated to the platform's conventional char sequence.
+** If bFlush true, flush the stream.
+**
+** An additional side-effect is that if the stream is one passed
+** to consoleClassifySetup() as an output, it is flushed first.
+**
+** Note that binary/text mode has no effect on console I/O
+** translation. On all platforms, newline to the console starts
+** a new line and CR,LF chars from the console become a newline.
+*/
+SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush);
+SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush);
+#endif
+
+#ifdef SQLITE_CIO_PROMPTED_IN
+typedef struct Prompts {
+ int numPrompts;
+ const char **azPrompts;
+} Prompts;
+
+/*
+** Macros for use of a line editor.
+**
+** The following macros define operations involving use of a
+** line-editing library or simple console interaction.
+** A "T" argument is a text (char *) buffer or filename.
+** A "N" argument is an integer.
+**
+** SHELL_ADD_HISTORY(T) // Record text as line(s) of history.
+** SHELL_READ_HISTORY(T) // Read history from file named by T.
+** SHELL_WRITE_HISTORY(T) // Write history to file named by T.
+** SHELL_STIFLE_HISTORY(N) // Limit history to N entries.
+**
+** A console program which does interactive console input is
+** expected to call:
+** SHELL_READ_HISTORY(T) before collecting such input;
+** SHELL_ADD_HISTORY(T) as record-worthy input is taken;
+** SHELL_STIFLE_HISTORY(N) after console input ceases; then
+** SHELL_WRITE_HISTORY(T) before the program exits.
+*/
+
+/*
+** Retrieve a single line of input text from an input stream.
+**
+** If pfIn is the input stream passed to consoleClassifySetup(),
+** and azPrompt is not NULL, then a prompt is issued before the
+** line is collected, as selected by the isContinuation flag.
+** Array azPrompt[{0,1}] holds the {main,continuation} prompt.
+**
+** If zBufPrior is not NULL then it is a buffer from a prior
+** call to this routine that can be reused, or will be freed.
+**
+** The result is stored in space obtained from malloc() and
+** must either be freed by the caller or else passed back to
+** this function as zBufPrior for reuse.
+**
+** This function may call upon services of a line-editing
+** library to interactively collect line edited input.
+*/
+SQLITE_INTERNAL_LINKAGE char *
+shellGetLine(FILE *pfIn, char *zBufPrior, int nLen,
+ short isContinuation, Prompts azPrompt);
+#endif /* defined(SQLITE_CIO_PROMPTED_IN) */
+/*
+** TBD: Define an interface for application(s) to generate
+** completion candidates for use by the line-editor.
+**
+** This may be premature; the CLI is the only application
+** that does this. Yet, getting line-editing melded into
+** console I/O is desirable because a line-editing library
+** may have to establish console operating mode, possibly
+** in a way that interferes with the above functionality.
+*/
+
+#if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))
+/* Skip over as much z[] input char sequence as is valid UTF-8,
+** limited per nAccept char's or whole characters and containing
+** no char cn such that ((1<=0 => char count, nAccept<0 => character
+ */
+SQLITE_INTERNAL_LINKAGE const char*
+zSkipValidUtf8(const char *z, int nAccept, long ccm);
+
+#endif
diff --git a/sqlite/ext/expert/README.md b/sqlite/ext/expert/README.md
new file mode 100644
index 00000000..28886fd1
--- /dev/null
+++ b/sqlite/ext/expert/README.md
@@ -0,0 +1,83 @@
+## SQLite Expert Extension
+
+This folder contains code for a simple system to propose useful indexes
+given a database and a set of SQL queries. It works as follows:
+
+ 1. The user database schema is copied to a temporary database.
+
+ 1. All SQL queries are prepared against the temporary database.
+ Information regarding the WHERE and ORDER BY clauses, and other query
+ features that affect index selection are recorded.
+
+ 1. The information gathered in step 2 is used to create candidate
+ indexes - indexes that the planner might have made use of in the previous
+ step, had they been available.
+
+ 1. A subset of the data in the user database is used to generate statistics
+ for all existing indexes and the candidate indexes generated in step 3
+ above.
+
+ 1. The SQL queries are prepared a second time. If the planner uses any
+ of the indexes created in step 3, they are recommended to the user.
+
+# C API
+
+The SQLite expert C API is defined in sqlite3expert.h. Most uses will proceed
+as follows:
+
+ 1. An sqlite3expert object is created by calling **sqlite3\_expert\_new()**.
+ A database handle opened by the user is passed as an argument.
+
+ 1. The sqlite3expert object is configured with one or more SQL statements
+ by making one or more calls to **sqlite3\_expert\_sql()**. Each call may
+ specify a single SQL statement, or multiple statements separated by
+ semi-colons.
+
+ 1. Optionally, the **sqlite3\_expert\_config()** API may be used to
+ configure the size of the data subset used to generate index statistics.
+ Using a smaller subset of the data can speed up the analysis.
+
+ 1. **sqlite3\_expert\_analyze()** is called to run the analysis.
+
+ 1. One or more calls are made to **sqlite3\_expert\_report()** to extract
+ components of the results of the analysis.
+
+ 1. **sqlite3\_expert\_destroy()** is called to free all resources.
+
+Refer to comments in sqlite3expert.h for further details.
+
+# sqlite3_expert application
+
+The file "expert.c" contains the code for a command line application that
+uses the API described above. It can be compiled with (for example):
+
+
+
+Assuming the database is named "test.db", it can then be run to analyze a
+single query:
+
+
+ ./sqlite3_expert -sql <sql-query> test.db
+
+
+Or an entire text file worth of queries with:
+
+
+ ./sqlite3_expert -file <text-file> test.db
+
+
+By default, sqlite3\_expert generates index statistics using all the data in
+the user database. For a large database, this may be prohibitively time
+consuming. The "-sample" option may be used to configure sqlite3\_expert to
+generate statistics based on an integer percentage of the user database as
+follows:
+
+
+ # Generate statistics based on 25% of the user database rows:
+ ./sqlite3_expert -sample 25 -sql <sql-query> test.db
+
+ # Do not generate any statistics at all:
+ ./sqlite3_expert -sample 0 -sql <sql-query> test.db
+
diff --git a/sqlite/ext/expert/expert.c b/sqlite/ext/expert/expert.c
new file mode 100644
index 00000000..051480f8
--- /dev/null
+++ b/sqlite/ext/expert/expert.c
@@ -0,0 +1,156 @@
+/*
+** 2017 April 07
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+
+
+#include
+#include
+#include
+#include
+#include "sqlite3expert.h"
+
+
+static void option_requires_argument(const char *zOpt){
+ fprintf(stderr, "Option requires an argument: %s\n", zOpt);
+ exit(-3);
+}
+
+static int option_integer_arg(const char *zVal){
+ return atoi(zVal);
+}
+
+static void usage(char **argv){
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Usage %s ?OPTIONS? DATABASE\n", argv[0]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Options are:\n");
+ fprintf(stderr, " -sql SQL (analyze SQL statements passed as argument)\n");
+ fprintf(stderr, " -file FILE (read SQL statements from file FILE)\n");
+ fprintf(stderr, " -verbose LEVEL (integer verbosity level. default 1)\n");
+ fprintf(stderr, " -sample PERCENT (percent of db to sample. default 100)\n");
+ exit(-1);
+}
+
+static int readSqlFromFile(sqlite3expert *p, const char *zFile, char **pzErr){
+ FILE *in = fopen(zFile, "rb");
+ long nIn;
+ size_t nRead;
+ char *pBuf;
+ int rc;
+ if( in==0 ){
+ *pzErr = sqlite3_mprintf("failed to open file %s\n", zFile);
+ return SQLITE_ERROR;
+ }
+ fseek(in, 0, SEEK_END);
+ nIn = ftell(in);
+ rewind(in);
+ pBuf = sqlite3_malloc64( nIn+1 );
+ nRead = fread(pBuf, nIn, 1, in);
+ fclose(in);
+ if( nRead!=1 ){
+ sqlite3_free(pBuf);
+ *pzErr = sqlite3_mprintf("failed to read file %s\n", zFile);
+ return SQLITE_ERROR;
+ }
+ pBuf[nIn] = 0;
+ rc = sqlite3_expert_sql(p, pBuf, pzErr);
+ sqlite3_free(pBuf);
+ return rc;
+}
+
+int main(int argc, char **argv){
+ const char *zDb;
+ int rc = 0;
+ char *zErr = 0;
+ int i;
+ int iVerbose = 1; /* -verbose option */
+
+ sqlite3 *db = 0;
+ sqlite3expert *p = 0;
+
+ if( argc<2 ) usage(argv);
+ zDb = argv[argc-1];
+ if( zDb[0]=='-' ) usage(argv);
+ rc = sqlite3_open(zDb, &db);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "Cannot open db file: %s - %s\n", zDb, sqlite3_errmsg(db));
+ exit(-2);
+ }
+
+ p = sqlite3_expert_new(db, &zErr);
+ if( p==0 ){
+ fprintf(stderr, "Cannot run analysis: %s\n", zErr);
+ rc = 1;
+ }else{
+ for(i=1; i<(argc-1); i++){
+ char *zArg = argv[i];
+ int nArg;
+ if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++;
+ nArg = (int)strlen(zArg);
+ if( nArg>=2 && 0==sqlite3_strnicmp(zArg, "-file", nArg) ){
+ if( ++i==(argc-1) ) option_requires_argument("-file");
+ rc = readSqlFromFile(p, argv[i], &zErr);
+ }
+
+ else if( nArg>=3 && 0==sqlite3_strnicmp(zArg, "-sql", nArg) ){
+ if( ++i==(argc-1) ) option_requires_argument("-sql");
+ rc = sqlite3_expert_sql(p, argv[i], &zErr);
+ }
+
+ else if( nArg>=3 && 0==sqlite3_strnicmp(zArg, "-sample", nArg) ){
+ int iSample;
+ if( ++i==(argc-1) ) option_requires_argument("-sample");
+ iSample = option_integer_arg(argv[i]);
+ sqlite3_expert_config(p, EXPERT_CONFIG_SAMPLE, iSample);
+ }
+
+ else if( nArg>=2 && 0==sqlite3_strnicmp(zArg, "-verbose", nArg) ){
+ if( ++i==(argc-1) ) option_requires_argument("-verbose");
+ iVerbose = option_integer_arg(argv[i]);
+ }
+
+ else{
+ usage(argv);
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_expert_analyze(p, &zErr);
+ }
+
+ if( rc==SQLITE_OK ){
+ int nQuery = sqlite3_expert_count(p);
+ if( iVerbose>0 ){
+ const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
+ fprintf(stdout, "-- Candidates -------------------------------\n");
+ fprintf(stdout, "%s\n", zCand);
+ }
+ for(i=0; i0 ){
+ fprintf(stdout, "-- Query %d ----------------------------------\n",i+1);
+ fprintf(stdout, "%s\n\n", zSql);
+ }
+ fprintf(stdout, "%s\n%s\n", zIdx, zEQP);
+ }
+ }else{
+ fprintf(stderr, "Error: %s\n", zErr ? zErr : "?");
+ }
+
+ sqlite3_expert_destroy(p);
+ sqlite3_free(zErr);
+ return rc;
+}
diff --git a/sqlite/ext/expert/expert1.test b/sqlite/ext/expert/expert1.test
new file mode 100644
index 00000000..0c3b512a
--- /dev/null
+++ b/sqlite/ext/expert/expert1.test
@@ -0,0 +1,609 @@
+# 2009 Nov 11
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# TESTRUNNER: shell
+#
+# The focus of this file is testing the CLI shell tool. Specifically,
+# the ".recommend" command.
+#
+#
+
+# Test plan:
+#
+#
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source $testdir/tester.tcl
+set testprefix expert1
+
+if {[info commands sqlite3_expert_new]==""} {
+ finish_test
+ return
+}
+
+
+set CLI [test_binary_name sqlite3]
+set CMD [test_binary_name sqlite3_expert]
+
+proc squish {txt} {
+ regsub -all {[[:space:]]+} $txt { }
+}
+
+proc do_setup_rec_test {tn setup sql res} {
+ reset_db
+ if {[info exists ::set_main_db_name]} {
+ dbconfig_maindbname_icecube db
+ }
+ db eval $setup
+ uplevel [list do_rec_test $tn $sql $res]
+}
+
+foreach {tn setup} {
+ 1 {
+ if {![file executable $CMD]} { continue }
+
+ proc do_rec_test {tn sql res} {
+ set res [squish [string trim $res]]
+ set tst [subst -nocommands {
+ squish [string trim [exec $::CMD -verbose 0 -sql {$sql;} test.db]]
+ }]
+ uplevel [list do_test $tn $tst $res]
+ }
+ }
+ 2 {
+ if {[info commands sqlite3_expert_new]==""} { continue }
+
+ proc do_rec_test {tn sql res} {
+ set expert [sqlite3_expert_new db]
+ $expert sql $sql
+ $expert analyze
+
+ set result [list]
+ for {set i 0} {$i < [$expert count]} {incr i} {
+ set idx [string trim [$expert report $i indexes]]
+ if {$idx==""} {set idx "(no new indexes)"}
+ lappend result $idx
+ lappend result [string trim [$expert report $i plan]]
+ }
+
+ $expert destroy
+
+ set tst [subst -nocommands {set {} [squish [join {$result}]]}]
+ uplevel [list do_test $tn $tst [string trim [squish $res]]]
+ }
+ }
+ 3 {
+ if {[info commands sqlite3_expert_new]==""} { continue }
+ set ::set_main_db_name 1
+ }
+ 4 {
+ if {![file executable $CLI]} { continue }
+
+ proc do_rec_test {tn sql res} {
+ set res [squish [string trim $res]]
+ set tst [subst -nocommands {
+ squish [string trim [exec $::CLI test.db ".expert" {$sql;}]]
+ }]
+ uplevel [list do_test $tn $tst $res]
+ }
+ }
+} {
+
+ eval $setup
+
+
+do_setup_rec_test $tn.1 { CREATE TABLE t1(a, b, c) } {
+ SELECT * FROM t1
+} {
+ (no new indexes)
+ SCAN t1
+}
+
+do_setup_rec_test $tn.2 {
+ CREATE TABLE t1(a, b, c);
+} {
+ SELECT * FROM t1 WHERE b>?;
+} {
+ CREATE INDEX t1_idx_00000062 ON t1(b);
+ SEARCH t1 USING INDEX t1_idx_00000062 (b>?)
+}
+
+do_setup_rec_test $tn.3 {
+ CREATE TABLE t1(a, b, c);
+} {
+ SELECT * FROM t1 WHERE b COLLATE nocase BETWEEN ? AND ?
+} {
+ CREATE INDEX t1_idx_3e094c27 ON t1(b COLLATE NOCASE);
+ SEARCH t1 USING INDEX t1_idx_3e094c27 (b>? AND b)
+}
+
+do_setup_rec_test $tn.4 {
+ CREATE TABLE t1(a, b, c);
+} {
+ SELECT a FROM t1 ORDER BY b;
+} {
+ CREATE INDEX t1_idx_00000062 ON t1(b);
+ SCAN t1 USING INDEX t1_idx_00000062
+}
+
+do_setup_rec_test $tn.5 {
+ CREATE TABLE t1(a, b, c);
+} {
+ SELECT a FROM t1 WHERE a=? ORDER BY b;
+} {
+ CREATE INDEX t1_idx_000123a7 ON t1(a, b);
+ SEARCH t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
+}
+
+if 0 {
+do_setup_rec_test $tn.6 {
+ CREATE TABLE t1(a, b, c);
+} {
+ SELECT min(a) FROM t1
+} {
+ CREATE INDEX t1_idx_00000061 ON t1(a);
+ SEARCH t1 USING COVERING INDEX t1_idx_00000061
+}
+}
+
+do_setup_rec_test $tn.7 {
+ CREATE TABLE t1(a, b, c);
+} {
+ SELECT * FROM t1 ORDER BY a, b, c;
+} {
+ CREATE INDEX t1_idx_033e95fe ON t1(a, b, c);
+ SCAN t1 USING COVERING INDEX t1_idx_033e95fe
+}
+
+#do_setup_rec_test $tn.1.8 {
+# CREATE TABLE t1(a, b, c);
+#} {
+# SELECT * FROM t1 ORDER BY a ASC, b COLLATE nocase DESC, c ASC;
+#} {
+# CREATE INDEX t1_idx_5be6e222 ON t1(a, b COLLATE NOCASE DESC, c);
+# 0|0|0|SCAN t1 USING COVERING INDEX t1_idx_5be6e222
+#}
+
+do_setup_rec_test $tn.8.1 {
+ CREATE TABLE t1(a COLLATE NOCase, b, c);
+} {
+ SELECT * FROM t1 WHERE a=?
+} {
+ CREATE INDEX t1_idx_00000061 ON t1(a);
+ SEARCH t1 USING INDEX t1_idx_00000061 (a=?)
+}
+do_setup_rec_test $tn.8.2 {
+ CREATE TABLE t1(a, b COLLATE nocase, c);
+} {
+ SELECT * FROM t1 ORDER BY a ASC, b DESC, c ASC;
+} {
+ CREATE INDEX t1_idx_5cb97285 ON t1(a, b DESC, c);
+ SCAN t1 USING COVERING INDEX t1_idx_5cb97285
+}
+
+
+# Tables with names that require quotes.
+#
+do_setup_rec_test $tn.9.1 {
+ CREATE TABLE "t t"(a, b, c);
+} {
+ SELECT * FROM "t t" WHERE a=?
+} {
+ CREATE INDEX "t t_idx_00000061" ON "t t"(a);
+ SEARCH t t USING INDEX t t_idx_00000061 (a=?)
+}
+
+do_setup_rec_test $tn.9.2 {
+ CREATE TABLE "t t"(a, b, c);
+} {
+ SELECT * FROM "t t" WHERE b BETWEEN ? AND ?
+} {
+ CREATE INDEX "t t_idx_00000062" ON "t t"(b);
+ SEARCH t t USING INDEX t t_idx_00000062 (b>? AND b)
+}
+
+# Columns with names that require quotes.
+#
+do_setup_rec_test $tn.10.1 {
+ CREATE TABLE t3(a, "b b", c);
+} {
+ SELECT * FROM t3 WHERE "b b" = ?
+} {
+ CREATE INDEX t3_idx_00050c52 ON t3('b b');
+ SEARCH t3 USING INDEX t3_idx_00050c52 (b b=?)
+}
+
+do_setup_rec_test $tn.10.2 {
+ CREATE TABLE t3(a, "b b", c);
+} {
+ SELECT * FROM t3 ORDER BY "b b"
+} {
+ CREATE INDEX t3_idx_00050c52 ON t3('b b');
+ SCAN t3 USING INDEX t3_idx_00050c52
+}
+
+# Transitive constraints
+#
+do_setup_rec_test $tn.11.1 {
+ CREATE TABLE t5(a, b);
+ CREATE TABLE t6(c, d);
+} {
+ SELECT * FROM t5, t6 WHERE a=? AND b=c AND c=?
+} {
+ CREATE INDEX t5_idx_000123a7 ON t5(a, b);
+ CREATE INDEX t6_idx_00000063 ON t6(c);
+ SEARCH t6 USING INDEX t6_idx_00000063 (c=?)
+ SEARCH t5 USING COVERING INDEX t5_idx_000123a7 (a=? AND b=?)
+}
+
+# OR terms.
+#
+do_setup_rec_test $tn.12.1 {
+ CREATE TABLE t7(a, b);
+} {
+ SELECT * FROM t7 WHERE a=? OR b=?
+} {
+ CREATE INDEX t7_idx_00000062 ON t7(b);
+ CREATE INDEX t7_idx_00000061 ON t7(a);
+ MULTI-INDEX OR
+ INDEX 1
+ SEARCH t7 USING INDEX t7_idx_00000061 (a=?)
+ INDEX 2
+ SEARCH t7 USING INDEX t7_idx_00000062 (b=?)
+}
+
+# rowid terms.
+#
+do_setup_rec_test $tn.13.1 {
+ CREATE TABLE t8(a, b);
+} {
+ SELECT * FROM t8 WHERE rowid=?
+} {
+ (no new indexes)
+ SEARCH t8 USING INTEGER PRIMARY KEY (rowid=?)
+}
+do_setup_rec_test $tn.13.2 {
+ CREATE TABLE t8(a, b);
+} {
+ SELECT * FROM t8 ORDER BY rowid
+} {
+ (no new indexes)
+ SCAN t8
+}
+do_setup_rec_test $tn.13.3 {
+ CREATE TABLE t8(a, b);
+} {
+ SELECT * FROM t8 WHERE a=? ORDER BY rowid
+} {
+ CREATE INDEX t8_idx_00000061 ON t8(a);
+ SEARCH t8 USING INDEX t8_idx_00000061 (a=?)
+}
+
+# Triggers
+#
+do_setup_rec_test $tn.14 {
+ CREATE TABLE t9(a, b, c);
+ CREATE TABLE t10(a, b, c);
+ CREATE TRIGGER t9t AFTER INSERT ON t9 BEGIN
+ UPDATE t10 SET a=new.a WHERE b = new.b;
+ END;
+} {
+ INSERT INTO t9 VALUES(?, ?, ?);
+} {
+ CREATE INDEX t10_idx_00000062 ON t10(b);
+ SEARCH t10 USING INDEX t10_idx_00000062 (b=?)
+}
+
+do_setup_rec_test $tn.15 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+
+ WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
+ INSERT INTO t1 SELECT (i-1)/50, (i-1)/20 FROM s;
+
+ WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
+ INSERT INTO t2 SELECT (i-1)/20, (i-1)/5 FROM s;
+} {
+ SELECT * FROM t2, t1 WHERE b=? AND d=? AND t2.rowid=t1.rowid
+} {
+ CREATE INDEX t2_idx_00000064 ON t2(d);
+ SEARCH t2 USING INDEX t2_idx_00000064 (d=?)
+ SEARCH t1 USING INTEGER PRIMARY KEY (rowid=?)
+}
+
+do_setup_rec_test $tn.16 {
+ CREATE TABLE t1(a, b);
+} {
+ SELECT * FROM t1 WHERE b IS NOT NULL;
+} {
+ (no new indexes)
+ SCAN t1
+}
+
+do_setup_rec_test $tn.17.1 {
+ CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
+} {
+ SELECT * FROM example WHERE a=?
+} {
+ (no new indexes)
+ SEARCH example USING INDEX sqlite_autoindex_example_1 (A=?)
+}
+do_setup_rec_test $tn.17.2 {
+ CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
+} {
+ SELECT * FROM example WHERE b=?
+} {
+ CREATE INDEX example_idx_00000042 ON example(B);
+ SEARCH example USING INDEX example_idx_00000042 (B=?)
+}
+do_setup_rec_test $tn.17.3 {
+ CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
+} {
+ SELECT * FROM example WHERE a=? AND b=?
+} {
+ (no new indexes)
+ SEARCH example USING INDEX sqlite_autoindex_example_1 (A=? AND B=?)
+}
+do_setup_rec_test $tn.17.4 {
+ CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
+} {
+ SELECT * FROM example WHERE a=? AND b>?
+} {
+ (no new indexes)
+ SEARCH example USING INDEX sqlite_autoindex_example_1 (A=? AND B>?)
+}
+do_setup_rec_test $tn.17.5 {
+ CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
+} {
+ SELECT * FROM example WHERE a>? AND b=?
+} {
+ CREATE INDEX example_idx_0000cb3f ON example(B, A);
+ SEARCH example USING INDEX example_idx_0000cb3f (B=? AND A>?)
+}
+
+do_setup_rec_test $tn.18.0 {
+ CREATE TABLE SomeObject (
+ a INTEGER PRIMARY KEY,
+ x TEXT GENERATED ALWAYS AS(HEX(a)) VIRTUAL
+ );
+} {
+ SELECT x FROM SomeObject;
+} {
+ (no new indexes)
+ SCAN SomeObject
+}
+do_setup_rec_test $tn.18.1 {
+ CREATE TABLE SomeObject (
+ a INTEGER PRIMARY KEY,
+ x TEXT GENERATED ALWAYS AS(HEX(a)) VIRTUAL
+ );
+} {
+ SELECT * FROM SomeObject WHERE x=?;
+} {
+ CREATE INDEX SomeObject_idx_00000078 ON SomeObject(x);
+ SEARCH SomeObject USING COVERING INDEX SomeObject_idx_00000078 (x=?)
+}
+
+
+do_setup_rec_test $tn.19.0 {
+ CREATE TABLE t1("index");
+} {
+ SELECT * FROM t1 ORDER BY "index";
+} {
+ CREATE INDEX t1_idx_01a7214e ON t1('index');
+ SCAN t1 USING COVERING INDEX t1_idx_01a7214e
+}
+
+ifcapable fts5 {
+ do_setup_rec_test $tn.20.0 {
+ CREATE VIRTUAL TABLE ft USING fts5(a);
+ CREATE TABLE t1(x, y);
+ } {
+ SELECT * FROM ft, t1 WHERE a=x
+ } {
+ CREATE INDEX t1_idx_00000078 ON t1(x);
+ SCAN ft VIRTUAL TABLE INDEX 0:
+ SEARCH t1 USING INDEX t1_idx_00000078 (x=?)
+ }
+}
+
+}
+
+proc do_candidates_test {tn sql res} {
+ set res [squish [string trim $res]]
+
+ set expert [sqlite3_expert_new db]
+ $expert sql $sql
+ $expert analyze
+
+ set candidates [squish [string trim [$expert report 0 candidates]]]
+ $expert destroy
+
+ uplevel [list do_test $tn [list set {} $candidates] $res]
+}
+
+
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+
+ WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
+ INSERT INTO t1 SELECT (i-1)/50, (i-1)/20 FROM s;
+
+ WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
+ INSERT INTO t2 SELECT (i-1)/20, (i-1)/5 FROM s;
+
+ CREATE INDEX i1 ON t1( lower(a) );
+}
+do_candidates_test 5.1 {
+ SELECT * FROM t1,t2 WHERE (b=? OR a=?) AND (c=? OR d=?)
+} {
+ CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
+ CREATE INDEX t1_idx_00000061 ON t1(a); -- stat1: 100 50
+ CREATE INDEX t2_idx_00000063 ON t2(c); -- stat1: 100 20
+ CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
+}
+
+do_candidates_test 5.2 {
+ SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=?
+} {
+ CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 17
+ CREATE INDEX t2_idx_0001295b ON t2(c, d); -- stat1: 100 20 5
+}
+
+do_execsql_test 5.3 {
+ CREATE INDEX t1_idx_00000061 ON t1(a); -- stat1: 100 50
+ CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
+ CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 16
+
+ CREATE INDEX t2_idx_00000063 ON t2(c); -- stat1: 100 20
+ CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
+ CREATE INDEX t2_idx_0001295b ON t2(c, d); -- stat1: 100 20 5
+
+ ANALYZE;
+ SELECT * FROM sqlite_stat1 ORDER BY 1, 2;
+} {
+ t1 i1 {100 50}
+ t1 t1_idx_00000061 {100 50}
+ t1 t1_idx_00000062 {100 20}
+ t1 t1_idx_000123a7 {100 50 17}
+ t2 t2_idx_00000063 {100 20}
+ t2 t2_idx_00000064 {100 5}
+ t2 t2_idx_0001295b {100 20 5}
+}
+
+do_catchsql_test 5.4 {
+ SELECT sqlite_expert_rem(123, 123);
+} {1 {no such function: sqlite_expert_rem}}
+do_catchsql_test 5.5 {
+ SELECT sqlite_expert_sample();
+} {1 {no such function: sqlite_expert_sample}}
+
+if 0 {
+do_test expert1-6.0 {
+ catchcmd :memory: {
+.expert
+select base64('');
+.expert
+select name from pragma_collation_list order by name collate uint;
+}
+} {0 {(no new indexes)
+
+SCAN CONSTANT ROW
+
+(no new indexes)
+
+SCAN pragma_collation_list VIRTUAL TABLE INDEX 0:
+USE TEMP B-TREE FOR ORDER BY
+}}
+}
+
+do_execsql_test 6.0 {
+ CREATE TABLE x1(a, b, c, d);
+ CREATE INDEX x1ab ON x1(a, lower(b));
+ CREATE INDEX x1dcba ON x1(d, b+c, a);
+}
+
+do_candidates_test 6.1 {
+ SELECT * FROM x1 WHERE b=? ORDER BY a;
+} {
+ CREATE INDEX x1_idx_0001267f ON x1(b, a);
+ CREATE INDEX x1_idx_00000062 ON x1(b);
+}
+
+#-------------------------------------------------------------------------
+ifcapable fts5 {
+ reset_db
+ do_execsql_test 7.0 {
+ CREATE VIRTUAL TABLE ft USING fts5(a);
+ CREATE TABLE t1(x, y);
+ }
+
+ do_candidates_test 7.1 {
+ SELECT * FROM ft, t1 WHERE a=x
+ } {
+ CREATE INDEX t1_idx_00000078 ON t1(x);
+ }
+
+ register_tcl_module db
+ proc vtab_command {method args} {
+ global G
+
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE t1(a, b, c);"
+ }
+
+ xBestIndex {
+ return [list]
+ }
+
+ xFilter {
+ return [list sql "SELECT rowid, * FROM t0"]
+ }
+ }
+
+ return {}
+ }
+
+ do_execsql_test 7.2 {
+ CREATE TABLE t0(a, b, c);
+ INSERT INTO t0 VALUES(1, 2, 3), (11, 22, 33);
+ CREATE VIRTUAL TABLE t2 USING tcl(vtab_command);
+ }
+
+ do_execsql_test 7.3 {
+ SELECT * FROM t2
+ } {
+ 1 2 3
+ 11 22 33
+ }
+
+ do_candidates_test 7.4 {
+ SELECT * FROM ft, t1 WHERE a=x
+ } {
+ CREATE INDEX t1_idx_00000078 ON t1(x);
+ }
+
+ do_test 7.5 {
+ set expert [sqlite3_expert_new db]
+ list [catch { $expert sql "SELECT * FROM ft, t2 WHERE b=1" } msg] $msg
+ } {1 {no such table: t2}}
+ $expert destroy
+
+ reset_db
+ do_execsql_test 7.6 {
+ BEGIN TRANSACTION;
+ CREATE TABLE IF NOT EXISTS 'bfts_idx_data'(id INTEGER PRIMARY KEY, block BLOB);
+ CREATE TABLE IF NOT EXISTS 'fts_idx_data'(id INTEGER PRIMARY KEY, block BLOB);
+ INSERT INTO fts_idx_data VALUES(1,X'');
+ INSERT INTO fts_idx_data VALUES(10,X'00000000ff000001000000');
+ CREATE TABLE IF NOT EXISTS 'fts_idx_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
+ CREATE TABLE IF NOT EXISTS 'fts_idx_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER);
+ CREATE TABLE IF NOT EXISTS 'fts_idx_config'(k PRIMARY KEY, v) WITHOUT ROWID;
+ INSERT INTO fts_idx_config VALUES('version',4);
+ PRAGMA writable_schema=ON;
+ INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)VALUES('table','fts_idx','fts_idx',0,'CREATE VIRTUAL TABLE fts_idx USING fts5(Title, Description, Channel, Tags, content='''', contentless_delete=1)');
+
+ CREATE TABLE f(x BLOB, y);
+ COMMIT;
+ PRAGMA writable_schema = RESET;
+ }
+
+ do_candidates_test 7.4 {
+ SELECT * FROM fts_idx, f WHERE x = fts_idx.Channel
+ } {
+ CREATE INDEX f_idx_00000078 ON f(x);
+ }
+}
+
+finish_test
diff --git a/sqlite/ext/expert/sqlite3expert.c b/sqlite/ext/expert/sqlite3expert.c
new file mode 100644
index 00000000..93693cfa
--- /dev/null
+++ b/sqlite/ext/expert/sqlite3expert.c
@@ -0,0 +1,2234 @@
+/*
+** 2017 April 09
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+#include "sqlite3expert.h"
+#include
+#include
+#include
+
+#if !defined(SQLITE_AMALGAMATION)
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
+#endif
+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
+# define ALWAYS(X) (1)
+# define NEVER(X) (0)
+#elif !defined(NDEBUG)
+# define ALWAYS(X) ((X)?1:(assert(0),0))
+# define NEVER(X) ((X)?(assert(0),1):0)
+#else
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
+#endif
+#endif /* !defined(SQLITE_AMALGAMATION) */
+
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+typedef sqlite3_int64 i64;
+typedef sqlite3_uint64 u64;
+
+typedef struct IdxColumn IdxColumn;
+typedef struct IdxConstraint IdxConstraint;
+typedef struct IdxScan IdxScan;
+typedef struct IdxStatement IdxStatement;
+typedef struct IdxTable IdxTable;
+typedef struct IdxWrite IdxWrite;
+
+#define STRLEN (int)strlen
+
+/*
+** A temp table name that we assume no user database will actually use.
+** If this assumption proves incorrect triggers on the table with the
+** conflicting name will be ignored.
+*/
+#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776"
+
+/*
+** A single constraint. Equivalent to either "col = ?" or "col < ?" (or
+** any other type of single-ended range constraint on a column).
+**
+** pLink:
+** Used to temporarily link IdxConstraint objects into lists while
+** creating candidate indexes.
+*/
+struct IdxConstraint {
+ char *zColl; /* Collation sequence */
+ int bRange; /* True for range, false for eq */
+ int iCol; /* Constrained table column */
+ int bFlag; /* Used by idxFindCompatible() */
+ int bDesc; /* True if ORDER BY DESC */
+ IdxConstraint *pNext; /* Next constraint in pEq or pRange list */
+ IdxConstraint *pLink; /* See above */
+};
+
+/*
+** A single scan of a single table.
+*/
+struct IdxScan {
+ IdxTable *pTab; /* Associated table object */
+ int iDb; /* Database containing table zTable */
+ i64 covering; /* Mask of columns required for cov. index */
+ IdxConstraint *pOrder; /* ORDER BY columns */
+ IdxConstraint *pEq; /* List of == constraints */
+ IdxConstraint *pRange; /* List of < constraints */
+ IdxScan *pNextScan; /* Next IdxScan object for same analysis */
+};
+
+/*
+** Information regarding a single database table. Extracted from
+** "PRAGMA table_info" by function idxGetTableInfo().
+*/
+struct IdxColumn {
+ char *zName;
+ char *zColl;
+ int iPk;
+};
+struct IdxTable {
+ int nCol;
+ char *zName; /* Table name */
+ IdxColumn *aCol;
+ IdxTable *pNext; /* Next table in linked list of all tables */
+};
+
+/*
+** An object of the following type is created for each unique table/write-op
+** seen. The objects are stored in a singly-linked list beginning at
+** sqlite3expert.pWrite.
+*/
+struct IdxWrite {
+ IdxTable *pTab;
+ int eOp; /* SQLITE_UPDATE, DELETE or INSERT */
+ IdxWrite *pNext;
+};
+
+/*
+** Each statement being analyzed is represented by an instance of this
+** structure.
+*/
+struct IdxStatement {
+ int iId; /* Statement number */
+ char *zSql; /* SQL statement */
+ char *zIdx; /* Indexes */
+ char *zEQP; /* Plan */
+ IdxStatement *pNext;
+};
+
+
+/*
+** A hash table for storing strings. With space for a payload string
+** with each entry. Methods are:
+**
+** idxHashInit()
+** idxHashClear()
+** idxHashAdd()
+** idxHashSearch()
+*/
+#define IDX_HASH_SIZE 1023
+typedef struct IdxHashEntry IdxHashEntry;
+typedef struct IdxHash IdxHash;
+struct IdxHashEntry {
+ char *zKey; /* nul-terminated key */
+ char *zVal; /* nul-terminated value string */
+ char *zVal2; /* nul-terminated value string 2 */
+ IdxHashEntry *pHashNext; /* Next entry in same hash bucket */
+ IdxHashEntry *pNext; /* Next entry in hash */
+};
+struct IdxHash {
+ IdxHashEntry *pFirst;
+ IdxHashEntry *aHash[IDX_HASH_SIZE];
+};
+
+/*
+** sqlite3expert object.
+*/
+struct sqlite3expert {
+ int iSample; /* Percentage of tables to sample for stat1 */
+ sqlite3 *db; /* User database */
+ sqlite3 *dbm; /* In-memory db for this analysis */
+ sqlite3 *dbv; /* Vtab schema for this analysis */
+ IdxTable *pTable; /* List of all IdxTable objects */
+ IdxScan *pScan; /* List of scan objects */
+ IdxWrite *pWrite; /* List of write objects */
+ IdxStatement *pStatement; /* List of IdxStatement objects */
+ int bRun; /* True once analysis has run */
+ char **pzErrmsg;
+ int rc; /* Error code from whereinfo hook */
+ IdxHash hIdx; /* Hash containing all candidate indexes */
+ char *zCandidates; /* For EXPERT_REPORT_CANDIDATES */
+};
+
+
+/*
+** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc().
+** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL.
+*/
+static void *idxMalloc(int *pRc, int nByte){
+ void *pRet;
+ assert( *pRc==SQLITE_OK );
+ assert( nByte>0 );
+ pRet = sqlite3_malloc(nByte);
+ if( pRet ){
+ memset(pRet, 0, nByte);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ return pRet;
+}
+
+/*
+** Initialize an IdxHash hash table.
+*/
+static void idxHashInit(IdxHash *pHash){
+ memset(pHash, 0, sizeof(IdxHash));
+}
+
+/*
+** Reset an IdxHash hash table.
+*/
+static void idxHashClear(IdxHash *pHash){
+ int i;
+ for(i=0; iaHash[i]; pEntry; pEntry=pNext){
+ pNext = pEntry->pHashNext;
+ sqlite3_free(pEntry->zVal2);
+ sqlite3_free(pEntry);
+ }
+ }
+ memset(pHash, 0, sizeof(IdxHash));
+}
+
+/*
+** Return the index of the hash bucket that the string specified by the
+** arguments to this function belongs.
+*/
+static int idxHashString(const char *z, int n){
+ unsigned int ret = 0;
+ int i;
+ for(i=0; i=0 );
+ for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
+ if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){
+ return 1;
+ }
+ }
+ pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + nKey+1 + nVal+1);
+ if( pEntry ){
+ pEntry->zKey = (char*)&pEntry[1];
+ memcpy(pEntry->zKey, zKey, nKey);
+ if( zVal ){
+ pEntry->zVal = &pEntry->zKey[nKey+1];
+ memcpy(pEntry->zVal, zVal, nVal);
+ }
+ pEntry->pHashNext = pHash->aHash[iHash];
+ pHash->aHash[iHash] = pEntry;
+
+ pEntry->pNext = pHash->pFirst;
+ pHash->pFirst = pEntry;
+ }
+ return 0;
+}
+
+/*
+** If zKey/nKey is present in the hash table, return a pointer to the
+** hash-entry object.
+*/
+static IdxHashEntry *idxHashFind(IdxHash *pHash, const char *zKey, int nKey){
+ int iHash;
+ IdxHashEntry *pEntry;
+ if( nKey<0 ) nKey = STRLEN(zKey);
+ iHash = idxHashString(zKey, nKey);
+ assert( iHash>=0 );
+ for(pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
+ if( STRLEN(pEntry->zKey)==nKey && 0==memcmp(pEntry->zKey, zKey, nKey) ){
+ return pEntry;
+ }
+ }
+ return 0;
+}
+
+/*
+** If the hash table contains an entry with a key equal to the string
+** passed as the final two arguments to this function, return a pointer
+** to the payload string. Otherwise, if zKey/nKey is not present in the
+** hash table, return NULL.
+*/
+static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
+ IdxHashEntry *pEntry = idxHashFind(pHash, zKey, nKey);
+ if( pEntry ) return pEntry->zVal;
+ return 0;
+}
+
+/*
+** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl
+** variable to point to a copy of nul-terminated string zColl.
+*/
+static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){
+ IdxConstraint *pNew;
+ int nColl = STRLEN(zColl);
+
+ assert( *pRc==SQLITE_OK );
+ pNew = (IdxConstraint*)idxMalloc(pRc, sizeof(IdxConstraint) * nColl + 1);
+ if( pNew ){
+ pNew->zColl = (char*)&pNew[1];
+ memcpy(pNew->zColl, zColl, nColl+1);
+ }
+ return pNew;
+}
+
+/*
+** An error associated with database handle db has just occurred. Pass
+** the error message to callback function xOut.
+*/
+static void idxDatabaseError(
+ sqlite3 *db, /* Database handle */
+ char **pzErrmsg /* Write error here */
+){
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+}
+
+/*
+** Prepare an SQL statement.
+*/
+static int idxPrepareStmt(
+ sqlite3 *db, /* Database handle to compile against */
+ sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
+ char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */
+ const char *zSql /* SQL statement to compile */
+){
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
+ if( rc!=SQLITE_OK ){
+ *ppStmt = 0;
+ idxDatabaseError(db, pzErrmsg);
+ }
+ return rc;
+}
+
+/*
+** Prepare an SQL statement using the results of a printf() formatting.
+*/
+static int idxPrintfPrepareStmt(
+ sqlite3 *db, /* Database handle to compile against */
+ sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
+ char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */
+ const char *zFmt, /* printf() format of SQL statement */
+ ... /* Trailing printf() arguments */
+){
+ va_list ap;
+ int rc;
+ char *zSql;
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = idxPrepareStmt(db, ppStmt, pzErrmsg, zSql);
+ sqlite3_free(zSql);
+ }
+ va_end(ap);
+ return rc;
+}
+
+
+/*************************************************************************
+** Beginning of virtual table implementation.
+*/
+typedef struct ExpertVtab ExpertVtab;
+struct ExpertVtab {
+ sqlite3_vtab base;
+ IdxTable *pTab;
+ sqlite3expert *pExpert;
+};
+
+typedef struct ExpertCsr ExpertCsr;
+struct ExpertCsr {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pData;
+};
+
+static char *expertDequote(const char *zIn){
+ int n = STRLEN(zIn);
+ char *zRet = sqlite3_malloc(n);
+
+ assert( zIn[0]=='\'' );
+ assert( zIn[n-1]=='\'' );
+
+ if( zRet ){
+ int iOut = 0;
+ int iIn = 0;
+ for(iIn=1; iIn<(n-1); iIn++){
+ if( zIn[iIn]=='\'' ){
+ assert( zIn[iIn+1]=='\'' );
+ iIn++;
+ }
+ zRet[iOut++] = zIn[iIn];
+ }
+ zRet[iOut] = '\0';
+ }
+
+ return zRet;
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the r-tree virtual table.
+**
+** argv[0] -> module name
+** argv[1] -> database name
+** argv[2] -> table name
+** argv[...] -> column names...
+*/
+static int expertConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ sqlite3expert *pExpert = (sqlite3expert*)pAux;
+ ExpertVtab *p = 0;
+ int rc;
+
+ if( argc!=4 ){
+ *pzErr = sqlite3_mprintf("internal error!");
+ rc = SQLITE_ERROR;
+ }else{
+ char *zCreateTable = expertDequote(argv[3]);
+ if( zCreateTable ){
+ rc = sqlite3_declare_vtab(db, zCreateTable);
+ if( rc==SQLITE_OK ){
+ p = idxMalloc(&rc, sizeof(ExpertVtab));
+ }
+ if( rc==SQLITE_OK ){
+ p->pExpert = pExpert;
+ p->pTab = pExpert->pTable;
+ assert( sqlite3_stricmp(p->pTab->zName, argv[2])==0 );
+ }
+ sqlite3_free(zCreateTable);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ *ppVtab = (sqlite3_vtab*)p;
+ return rc;
+}
+
+static int expertDisconnect(sqlite3_vtab *pVtab){
+ ExpertVtab *p = (ExpertVtab*)pVtab;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+static int expertBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pIdxInfo){
+ ExpertVtab *p = (ExpertVtab*)pVtab;
+ int rc = SQLITE_OK;
+ int n = 0;
+ IdxScan *pScan;
+ const int opmask =
+ SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_GT |
+ SQLITE_INDEX_CONSTRAINT_LT | SQLITE_INDEX_CONSTRAINT_GE |
+ SQLITE_INDEX_CONSTRAINT_LE;
+
+ pScan = idxMalloc(&rc, sizeof(IdxScan));
+ if( pScan ){
+ int i;
+
+ /* Link the new scan object into the list */
+ pScan->pTab = p->pTab;
+ pScan->pNextScan = p->pExpert->pScan;
+ p->pExpert->pScan = pScan;
+
+ /* Add the constraints to the IdxScan object */
+ for(i=0; inConstraint; i++){
+ struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
+ if( pCons->usable
+ && pCons->iColumn>=0
+ && p->pTab->aCol[pCons->iColumn].iPk==0
+ && (pCons->op & opmask)
+ ){
+ IdxConstraint *pNew;
+ const char *zColl = sqlite3_vtab_collation(pIdxInfo, i);
+ pNew = idxNewConstraint(&rc, zColl);
+ if( pNew ){
+ pNew->iCol = pCons->iColumn;
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ pNew->pNext = pScan->pEq;
+ pScan->pEq = pNew;
+ }else{
+ pNew->bRange = 1;
+ pNew->pNext = pScan->pRange;
+ pScan->pRange = pNew;
+ }
+ }
+ n++;
+ pIdxInfo->aConstraintUsage[i].argvIndex = n;
+ }
+ }
+
+ /* Add the ORDER BY to the IdxScan object */
+ for(i=pIdxInfo->nOrderBy-1; i>=0; i--){
+ int iCol = pIdxInfo->aOrderBy[i].iColumn;
+ if( iCol>=0 ){
+ IdxConstraint *pNew = idxNewConstraint(&rc, p->pTab->aCol[iCol].zColl);
+ if( pNew ){
+ pNew->iCol = iCol;
+ pNew->bDesc = pIdxInfo->aOrderBy[i].desc;
+ pNew->pNext = pScan->pOrder;
+ pNew->pLink = pScan->pOrder;
+ pScan->pOrder = pNew;
+ n++;
+ }
+ }
+ }
+ }
+
+ pIdxInfo->estimatedCost = 1000000.0 / (n+1);
+ return rc;
+}
+
+static int expertUpdate(
+ sqlite3_vtab *pVtab,
+ int nData,
+ sqlite3_value **azData,
+ sqlite_int64 *pRowid
+){
+ (void)pVtab;
+ (void)nData;
+ (void)azData;
+ (void)pRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Virtual table module xOpen method.
+*/
+static int expertOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ int rc = SQLITE_OK;
+ ExpertCsr *pCsr;
+ (void)pVTab;
+ pCsr = idxMalloc(&rc, sizeof(ExpertCsr));
+ *ppCursor = (sqlite3_vtab_cursor*)pCsr;
+ return rc;
+}
+
+/*
+** Virtual table module xClose method.
+*/
+static int expertClose(sqlite3_vtab_cursor *cur){
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
+ sqlite3_finalize(pCsr->pData);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+/*
+** Virtual table module xEof method.
+**
+** Return non-zero if the cursor does not currently point to a valid
+** record (i.e if the scan has finished), or zero otherwise.
+*/
+static int expertEof(sqlite3_vtab_cursor *cur){
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
+ return pCsr->pData==0;
+}
+
+/*
+** Virtual table module xNext method.
+*/
+static int expertNext(sqlite3_vtab_cursor *cur){
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
+ int rc = SQLITE_OK;
+
+ assert( pCsr->pData );
+ rc = sqlite3_step(pCsr->pData);
+ if( rc!=SQLITE_ROW ){
+ rc = sqlite3_finalize(pCsr->pData);
+ pCsr->pData = 0;
+ }else{
+ rc = SQLITE_OK;
+ }
+
+ return rc;
+}
+
+/*
+** Virtual table module xRowid method.
+*/
+static int expertRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ (void)cur;
+ *pRowid = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Virtual table module xColumn method.
+*/
+static int expertColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
+ sqlite3_value *pVal;
+ pVal = sqlite3_column_value(pCsr->pData, i);
+ if( pVal ){
+ sqlite3_result_value(ctx, pVal);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Virtual table module xFilter method.
+*/
+static int expertFilter(
+ sqlite3_vtab_cursor *cur,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ ExpertCsr *pCsr = (ExpertCsr*)cur;
+ ExpertVtab *pVtab = (ExpertVtab*)(cur->pVtab);
+ sqlite3expert *pExpert = pVtab->pExpert;
+ int rc;
+
+ (void)idxNum;
+ (void)idxStr;
+ (void)argc;
+ (void)argv;
+ rc = sqlite3_finalize(pCsr->pData);
+ pCsr->pData = 0;
+ if( rc==SQLITE_OK ){
+ rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
+ "SELECT * FROM main.%Q WHERE sqlite_expert_sample()", pVtab->pTab->zName
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = expertNext(cur);
+ }
+ return rc;
+}
+
+static int idxRegisterVtab(sqlite3expert *p){
+ static sqlite3_module expertModule = {
+ 2, /* iVersion */
+ expertConnect, /* xCreate - create a table */
+ expertConnect, /* xConnect - connect to an existing table */
+ expertBestIndex, /* xBestIndex - Determine search strategy */
+ expertDisconnect, /* xDisconnect - Disconnect from a table */
+ expertDisconnect, /* xDestroy - Drop a table */
+ expertOpen, /* xOpen - open a cursor */
+ expertClose, /* xClose - close a cursor */
+ expertFilter, /* xFilter - configure scan constraints */
+ expertNext, /* xNext - advance a cursor */
+ expertEof, /* xEof */
+ expertColumn, /* xColumn - read data */
+ expertRowid, /* xRowid - read data */
+ expertUpdate, /* xUpdate - write data */
+ 0, /* xBegin - begin transaction */
+ 0, /* xSync - sync transaction */
+ 0, /* xCommit - commit transaction */
+ 0, /* xRollback - rollback transaction */
+ 0, /* xFindFunction - function overloading */
+ 0, /* xRename - rename the table */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0, /* xIntegrity */
+ };
+
+ return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
+}
+/*
+** End of virtual table implementation.
+*************************************************************************/
+/*
+** Finalize SQL statement pStmt. If (*pRc) is SQLITE_OK when this function
+** is called, set it to the return value of sqlite3_finalize() before
+** returning. Otherwise, discard the sqlite3_finalize() return value.
+*/
+static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){
+ int rc = sqlite3_finalize(pStmt);
+ if( *pRc==SQLITE_OK ) *pRc = rc;
+}
+
+/*
+** Attempt to allocate an IdxTable structure corresponding to table zTab
+** in the main database of connection db. If successful, set (*ppOut) to
+** point to the new object and return SQLITE_OK. Otherwise, return an
+** SQLite error code and set (*ppOut) to NULL. In this case *pzErrmsg may be
+** set to point to an error string.
+**
+** It is the responsibility of the caller to eventually free either the
+** IdxTable object or error message using sqlite3_free().
+*/
+static int idxGetTableInfo(
+ sqlite3 *db, /* Database connection to read details from */
+ const char *zTab, /* Table name */
+ IdxTable **ppOut, /* OUT: New object (if successful) */
+ char **pzErrmsg /* OUT: Error message (if not) */
+){
+ sqlite3_stmt *p1 = 0;
+ int nCol = 0;
+ int nTab;
+ int nByte;
+ IdxTable *pNew = 0;
+ int rc, rc2;
+ char *pCsr = 0;
+ int nPk = 0;
+
+ *ppOut = 0;
+ if( zTab==0 ) return SQLITE_ERROR;
+ nTab = STRLEN(zTab);
+ nByte = sizeof(IdxTable) + nTab + 1;
+ rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q", zTab);
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
+ const char *zCol = (const char*)sqlite3_column_text(p1, 1);
+ const char *zColSeq = 0;
+ if( zCol==0 ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+ nByte += 1 + STRLEN(zCol);
+ rc = sqlite3_table_column_metadata(
+ db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
+ );
+ if( zColSeq==0 ) zColSeq = "binary";
+ nByte += 1 + STRLEN(zColSeq);
+ nCol++;
+ nPk += (sqlite3_column_int(p1, 5)>0);
+ }
+ rc2 = sqlite3_reset(p1);
+ if( rc==SQLITE_OK ) rc = rc2;
+
+ nByte += sizeof(IdxColumn) * nCol;
+ if( rc==SQLITE_OK ){
+ pNew = idxMalloc(&rc, nByte);
+ }
+ if( rc==SQLITE_OK ){
+ pNew->aCol = (IdxColumn*)&pNew[1];
+ pNew->nCol = nCol;
+ pCsr = (char*)&pNew->aCol[nCol];
+ }
+
+ nCol = 0;
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
+ const char *zCol = (const char*)sqlite3_column_text(p1, 1);
+ const char *zColSeq = 0;
+ int nCopy;
+ if( zCol==0 ) continue;
+ nCopy = STRLEN(zCol) + 1;
+ pNew->aCol[nCol].zName = pCsr;
+ pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5)==1 && nPk==1);
+ memcpy(pCsr, zCol, nCopy);
+ pCsr += nCopy;
+
+ rc = sqlite3_table_column_metadata(
+ db, "main", zTab, zCol, 0, &zColSeq, 0, 0, 0
+ );
+ if( rc==SQLITE_OK ){
+ if( zColSeq==0 ) zColSeq = "binary";
+ nCopy = STRLEN(zColSeq) + 1;
+ pNew->aCol[nCol].zColl = pCsr;
+ memcpy(pCsr, zColSeq, nCopy);
+ pCsr += nCopy;
+ }
+
+ nCol++;
+ }
+ idxFinalize(&rc, p1);
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pNew);
+ pNew = 0;
+ }else if( ALWAYS(pNew!=0) ){
+ pNew->zName = pCsr;
+ if( ALWAYS(pNew->zName!=0) ) memcpy(pNew->zName, zTab, nTab+1);
+ }
+
+ *ppOut = pNew;
+ return rc;
+}
+
+/*
+** This function is a no-op if *pRc is set to anything other than
+** SQLITE_OK when it is called.
+**
+** If *pRc is initially set to SQLITE_OK, then the text specified by
+** the printf() style arguments is appended to zIn and the result returned
+** in a buffer allocated by sqlite3_malloc(). sqlite3_free() is called on
+** zIn before returning.
+*/
+static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
+ va_list ap;
+ char *zAppend = 0;
+ char *zRet = 0;
+ int nIn = zIn ? STRLEN(zIn) : 0;
+ int nAppend = 0;
+ va_start(ap, zFmt);
+ if( *pRc==SQLITE_OK ){
+ zAppend = sqlite3_vmprintf(zFmt, ap);
+ if( zAppend ){
+ nAppend = STRLEN(zAppend);
+ zRet = (char*)sqlite3_malloc(nIn + nAppend + 1);
+ }
+ if( zAppend && zRet ){
+ if( nIn ) memcpy(zRet, zIn, nIn);
+ memcpy(&zRet[nIn], zAppend, nAppend+1);
+ }else{
+ sqlite3_free(zRet);
+ zRet = 0;
+ *pRc = SQLITE_NOMEM;
+ }
+ sqlite3_free(zAppend);
+ sqlite3_free(zIn);
+ }
+ va_end(ap);
+ return zRet;
+}
+
+/*
+** Return true if zId must be quoted in order to use it as an SQL
+** identifier, or false otherwise.
+*/
+static int idxIdentifierRequiresQuotes(const char *zId){
+ int i;
+ int nId = STRLEN(zId);
+
+ if( sqlite3_keyword_check(zId, nId) ) return 1;
+
+ for(i=0; zId[i]; i++){
+ if( !(zId[i]=='_')
+ && !(zId[i]>='0' && zId[i]<='9')
+ && !(zId[i]>='a' && zId[i]<='z')
+ && !(zId[i]>='A' && zId[i]<='Z')
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** This function appends an index column definition suitable for constraint
+** pCons to the string passed as zIn and returns the result.
+*/
+static char *idxAppendColDefn(
+ int *pRc, /* IN/OUT: Error code */
+ char *zIn, /* Column defn accumulated so far */
+ IdxTable *pTab, /* Table index will be created on */
+ IdxConstraint *pCons
+){
+ char *zRet = zIn;
+ IdxColumn *p = &pTab->aCol[pCons->iCol];
+ if( zRet ) zRet = idxAppendText(pRc, zRet, ", ");
+
+ if( idxIdentifierRequiresQuotes(p->zName) ){
+ zRet = idxAppendText(pRc, zRet, "%Q", p->zName);
+ }else{
+ zRet = idxAppendText(pRc, zRet, "%s", p->zName);
+ }
+
+ if( sqlite3_stricmp(p->zColl, pCons->zColl) ){
+ if( idxIdentifierRequiresQuotes(pCons->zColl) ){
+ zRet = idxAppendText(pRc, zRet, " COLLATE %Q", pCons->zColl);
+ }else{
+ zRet = idxAppendText(pRc, zRet, " COLLATE %s", pCons->zColl);
+ }
+ }
+
+ if( pCons->bDesc ){
+ zRet = idxAppendText(pRc, zRet, " DESC");
+ }
+ return zRet;
+}
+
+/*
+** Search database dbm for an index compatible with the one idxCreateFromCons()
+** would create from arguments pScan, pEq and pTail. If no error occurs and
+** such an index is found, return non-zero. Or, if no such index is found,
+** return zero.
+**
+** If an error occurs, set *pRc to an SQLite error code and return zero.
+*/
+static int idxFindCompatible(
+ int *pRc, /* OUT: Error code */
+ sqlite3* dbm, /* Database to search */
+ IdxScan *pScan, /* Scan for table to search for index on */
+ IdxConstraint *pEq, /* List of == constraints */
+ IdxConstraint *pTail /* List of range constraints */
+){
+ const char *zTbl = pScan->pTab->zName;
+ sqlite3_stmt *pIdxList = 0;
+ IdxConstraint *pIter;
+ int nEq = 0; /* Number of elements in pEq */
+ int rc;
+
+ /* Count the elements in list pEq */
+ for(pIter=pEq; pIter; pIter=pIter->pLink) nEq++;
+
+ rc = idxPrintfPrepareStmt(dbm, &pIdxList, 0, "PRAGMA index_list=%Q", zTbl);
+ while( rc==SQLITE_OK && sqlite3_step(pIdxList)==SQLITE_ROW ){
+ int bMatch = 1;
+ IdxConstraint *pT = pTail;
+ sqlite3_stmt *pInfo = 0;
+ const char *zIdx = (const char*)sqlite3_column_text(pIdxList, 1);
+ if( zIdx==0 ) continue;
+
+ /* Zero the IdxConstraint.bFlag values in the pEq list */
+ for(pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0;
+
+ rc = idxPrintfPrepareStmt(dbm, &pInfo, 0, "PRAGMA index_xInfo=%Q", zIdx);
+ while( rc==SQLITE_OK && sqlite3_step(pInfo)==SQLITE_ROW ){
+ int iIdx = sqlite3_column_int(pInfo, 0);
+ int iCol = sqlite3_column_int(pInfo, 1);
+ const char *zColl = (const char*)sqlite3_column_text(pInfo, 4);
+
+ if( iIdxpLink){
+ if( pIter->bFlag ) continue;
+ if( pIter->iCol!=iCol ) continue;
+ if( sqlite3_stricmp(pIter->zColl, zColl) ) continue;
+ pIter->bFlag = 1;
+ break;
+ }
+ if( pIter==0 ){
+ bMatch = 0;
+ break;
+ }
+ }else{
+ if( pT ){
+ if( pT->iCol!=iCol || sqlite3_stricmp(pT->zColl, zColl) ){
+ bMatch = 0;
+ break;
+ }
+ pT = pT->pLink;
+ }
+ }
+ }
+ idxFinalize(&rc, pInfo);
+
+ if( rc==SQLITE_OK && bMatch ){
+ sqlite3_finalize(pIdxList);
+ return 1;
+ }
+ }
+ idxFinalize(&rc, pIdxList);
+
+ *pRc = rc;
+ return 0;
+}
+
+/* Callback for sqlite3_exec() with query with leading count(*) column.
+ * The first argument is expected to be an int*, referent to be incremented
+ * if that leading column is not exactly '0'.
+ */
+static int countNonzeros(void* pCount, int nc,
+ char* azResults[], char* azColumns[]){
+ (void)azColumns; /* Suppress unused parameter warning */
+ if( nc>0 && (azResults[0][0]!='0' || azResults[0][1]!=0) ){
+ *((int *)pCount) += 1;
+ }
+ return 0;
+}
+
+static int idxCreateFromCons(
+ sqlite3expert *p,
+ IdxScan *pScan,
+ IdxConstraint *pEq,
+ IdxConstraint *pTail
+){
+ sqlite3 *dbm = p->dbm;
+ int rc = SQLITE_OK;
+ if( (pEq || pTail) && 0==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){
+ IdxTable *pTab = pScan->pTab;
+ char *zCols = 0;
+ char *zIdx = 0;
+ IdxConstraint *pCons;
+ unsigned int h = 0;
+ const char *zFmt;
+
+ for(pCons=pEq; pCons; pCons=pCons->pLink){
+ zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
+ }
+ for(pCons=pTail; pCons; pCons=pCons->pLink){
+ zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
+ }
+
+ if( rc==SQLITE_OK ){
+ /* Hash the list of columns to come up with a name for the index */
+ const char *zTable = pScan->pTab->zName;
+ int quoteTable = idxIdentifierRequiresQuotes(zTable);
+ char *zName = 0; /* Index name */
+ int collisions = 0;
+ do{
+ int i;
+ char *zFind;
+ for(i=0; zCols[i]; i++){
+ h += ((h<<3) + zCols[i]);
+ }
+ sqlite3_free(zName);
+ zName = sqlite3_mprintf("%s_idx_%08x", zTable, h);
+ if( zName==0 ) break;
+ /* Is is unique among table, view and index names? */
+ zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q"
+ " AND type in ('index','table','view')";
+ zFind = sqlite3_mprintf(zFmt, zName);
+ i = 0;
+ rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0);
+ assert(rc==SQLITE_OK);
+ sqlite3_free(zFind);
+ if( i==0 ){
+ collisions = 0;
+ break;
+ }
+ ++collisions;
+ }while( collisions<50 && zName!=0 );
+ if( collisions ){
+ /* This return means "Gave up trying to find a unique index name." */
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else if( zName==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ if( quoteTable ){
+ zFmt = "CREATE INDEX \"%w\" ON \"%w\"(%s)";
+ }else{
+ zFmt = "CREATE INDEX %s ON %s(%s)";
+ }
+ zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols);
+ if( !zIdx ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_exec(dbm, zIdx, 0, 0, p->pzErrmsg);
+ if( rc!=SQLITE_OK ){
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else{
+ idxHashAdd(&rc, &p->hIdx, zName, zIdx);
+ }
+ }
+ sqlite3_free(zName);
+ sqlite3_free(zIdx);
+ }
+ }
+
+ sqlite3_free(zCols);
+ }
+ return rc;
+}
+
+/*
+** Return true if list pList (linked by IdxConstraint.pLink) contains
+** a constraint compatible with *p. Otherwise return false.
+*/
+static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){
+ IdxConstraint *pCmp;
+ for(pCmp=pList; pCmp; pCmp=pCmp->pLink){
+ if( p->iCol==pCmp->iCol ) return 1;
+ }
+ return 0;
+}
+
+static int idxCreateFromWhere(
+ sqlite3expert *p,
+ IdxScan *pScan, /* Create indexes for this scan */
+ IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */
+){
+ IdxConstraint *p1 = 0;
+ IdxConstraint *pCon;
+ int rc;
+
+ /* Gather up all the == constraints. */
+ for(pCon=pScan->pEq; pCon; pCon=pCon->pNext){
+ if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
+ pCon->pLink = p1;
+ p1 = pCon;
+ }
+ }
+
+ /* Create an index using the == constraints collected above. And the
+ ** range constraint/ORDER BY terms passed in by the caller, if any. */
+ rc = idxCreateFromCons(p, pScan, p1, pTail);
+
+ /* If no range/ORDER BY passed by the caller, create a version of the
+ ** index for each range constraint. */
+ if( pTail==0 ){
+ for(pCon=pScan->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){
+ assert( pCon->pLink==0 );
+ if( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
+ rc = idxCreateFromCons(p, pScan, p1, pCon);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Create candidate indexes in database [dbm] based on the data in
+** linked-list pScan.
+*/
+static int idxCreateCandidates(sqlite3expert *p){
+ int rc = SQLITE_OK;
+ IdxScan *pIter;
+
+ for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
+ rc = idxCreateFromWhere(p, pIter, 0);
+ if( rc==SQLITE_OK && pIter->pOrder ){
+ rc = idxCreateFromWhere(p, pIter, pIter->pOrder);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Free all elements of the linked list starting at pConstraint.
+*/
+static void idxConstraintFree(IdxConstraint *pConstraint){
+ IdxConstraint *pNext;
+ IdxConstraint *p;
+
+ for(p=pConstraint; p; p=pNext){
+ pNext = p->pNext;
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Free all elements of the linked list starting from pScan up until pLast
+** (pLast is not freed).
+*/
+static void idxScanFree(IdxScan *pScan, IdxScan *pLast){
+ IdxScan *p;
+ IdxScan *pNext;
+ for(p=pScan; p!=pLast; p=pNext){
+ pNext = p->pNextScan;
+ idxConstraintFree(p->pOrder);
+ idxConstraintFree(p->pEq);
+ idxConstraintFree(p->pRange);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Free all elements of the linked list starting from pStatement up
+** until pLast (pLast is not freed).
+*/
+static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){
+ IdxStatement *p;
+ IdxStatement *pNext;
+ for(p=pStatement; p!=pLast; p=pNext){
+ pNext = p->pNext;
+ sqlite3_free(p->zEQP);
+ sqlite3_free(p->zIdx);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Free the linked list of IdxTable objects starting at pTab.
+*/
+static void idxTableFree(IdxTable *pTab){
+ IdxTable *pIter;
+ IdxTable *pNext;
+ for(pIter=pTab; pIter; pIter=pNext){
+ pNext = pIter->pNext;
+ sqlite3_free(pIter);
+ }
+}
+
+/*
+** Free the linked list of IdxWrite objects starting at pTab.
+*/
+static void idxWriteFree(IdxWrite *pTab){
+ IdxWrite *pIter;
+ IdxWrite *pNext;
+ for(pIter=pTab; pIter; pIter=pNext){
+ pNext = pIter->pNext;
+ sqlite3_free(pIter);
+ }
+}
+
+
+
+/*
+** This function is called after candidate indexes have been created. It
+** runs all the queries to see which indexes they prefer, and populates
+** IdxStatement.zIdx and IdxStatement.zEQP with the results.
+*/
+static int idxFindIndexes(
+ sqlite3expert *p,
+ char **pzErr /* OUT: Error message (sqlite3_malloc) */
+){
+ IdxStatement *pStmt;
+ sqlite3 *dbm = p->dbm;
+ int rc = SQLITE_OK;
+
+ IdxHash hIdx;
+ idxHashInit(&hIdx);
+
+ for(pStmt=p->pStatement; rc==SQLITE_OK && pStmt; pStmt=pStmt->pNext){
+ IdxHashEntry *pEntry;
+ sqlite3_stmt *pExplain = 0;
+ idxHashClear(&hIdx);
+ rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr,
+ "EXPLAIN QUERY PLAN %s", pStmt->zSql
+ );
+ while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){
+ /* int iId = sqlite3_column_int(pExplain, 0); */
+ /* int iParent = sqlite3_column_int(pExplain, 1); */
+ /* int iNotUsed = sqlite3_column_int(pExplain, 2); */
+ const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
+ int nDetail;
+ int i;
+
+ if( !zDetail ) continue;
+ nDetail = STRLEN(zDetail);
+
+ for(i=0; ihIdx, zIdx, nIdx);
+ if( zSql ){
+ idxHashAdd(&rc, &hIdx, zSql, 0);
+ if( rc ) goto find_indexes_out;
+ }
+ break;
+ }
+ }
+
+ if( zDetail[0]!='-' ){
+ pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n", zDetail);
+ }
+ }
+
+ for(pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
+ pStmt->zIdx = idxAppendText(&rc, pStmt->zIdx, "%s;\n", pEntry->zKey);
+ }
+
+ idxFinalize(&rc, pExplain);
+ }
+
+ find_indexes_out:
+ idxHashClear(&hIdx);
+ return rc;
+}
+
+static int idxAuthCallback(
+ void *pCtx,
+ int eOp,
+ const char *z3,
+ const char *z4,
+ const char *zDb,
+ const char *zTrigger
+){
+ int rc = SQLITE_OK;
+ (void)z4;
+ (void)zTrigger;
+ if( eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE || eOp==SQLITE_DELETE ){
+ if( sqlite3_stricmp(zDb, "main")==0 ){
+ sqlite3expert *p = (sqlite3expert*)pCtx;
+ IdxTable *pTab;
+ for(pTab=p->pTable; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_stricmp(z3, pTab->zName) ) break;
+ }
+ if( pTab ){
+ IdxWrite *pWrite;
+ for(pWrite=p->pWrite; pWrite; pWrite=pWrite->pNext){
+ if( pWrite->pTab==pTab && pWrite->eOp==eOp ) break;
+ }
+ if( pWrite==0 ){
+ pWrite = idxMalloc(&rc, sizeof(IdxWrite));
+ if( rc==SQLITE_OK ){
+ pWrite->pTab = pTab;
+ pWrite->eOp = eOp;
+ pWrite->pNext = p->pWrite;
+ p->pWrite = pWrite;
+ }
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+static int idxProcessOneTrigger(
+ sqlite3expert *p,
+ IdxWrite *pWrite,
+ char **pzErr
+){
+ static const char *zInt = UNIQUE_TABLE_NAME;
+ static const char *zDrop = "DROP TABLE " UNIQUE_TABLE_NAME;
+ IdxTable *pTab = pWrite->pTab;
+ const char *zTab = pTab->zName;
+ const char *zSql =
+ "SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema "
+ "WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
+ "ORDER BY type;";
+ sqlite3_stmt *pSelect = 0;
+ int rc = SQLITE_OK;
+ char *zWrite = 0;
+
+ /* Create the table and its triggers in the temp schema */
+ rc = idxPrintfPrepareStmt(p->db, &pSelect, pzErr, zSql, zTab, zTab);
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSelect) ){
+ const char *zCreate = (const char*)sqlite3_column_text(pSelect, 0);
+ if( zCreate==0 ) continue;
+ rc = sqlite3_exec(p->dbv, zCreate, 0, 0, pzErr);
+ }
+ idxFinalize(&rc, pSelect);
+
+ /* Rename the table in the temp schema to zInt */
+ if( rc==SQLITE_OK ){
+ char *z = sqlite3_mprintf("ALTER TABLE temp.%Q RENAME TO %Q", zTab, zInt);
+ if( z==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_exec(p->dbv, z, 0, 0, pzErr);
+ sqlite3_free(z);
+ }
+ }
+
+ switch( pWrite->eOp ){
+ case SQLITE_INSERT: {
+ int i;
+ zWrite = idxAppendText(&rc, zWrite, "INSERT INTO %Q VALUES(", zInt);
+ for(i=0; inCol; i++){
+ zWrite = idxAppendText(&rc, zWrite, "%s?", i==0 ? "" : ", ");
+ }
+ zWrite = idxAppendText(&rc, zWrite, ")");
+ break;
+ }
+ case SQLITE_UPDATE: {
+ int i;
+ zWrite = idxAppendText(&rc, zWrite, "UPDATE %Q SET ", zInt);
+ for(i=0; inCol; i++){
+ zWrite = idxAppendText(&rc, zWrite, "%s%Q=?", i==0 ? "" : ", ",
+ pTab->aCol[i].zName
+ );
+ }
+ break;
+ }
+ default: {
+ assert( pWrite->eOp==SQLITE_DELETE );
+ if( rc==SQLITE_OK ){
+ zWrite = sqlite3_mprintf("DELETE FROM %Q", zInt);
+ if( zWrite==0 ) rc = SQLITE_NOMEM;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pX = 0;
+ rc = sqlite3_prepare_v2(p->dbv, zWrite, -1, &pX, 0);
+ idxFinalize(&rc, pX);
+ if( rc!=SQLITE_OK ){
+ idxDatabaseError(p->dbv, pzErr);
+ }
+ }
+ sqlite3_free(zWrite);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(p->dbv, zDrop, 0, 0, pzErr);
+ }
+
+ return rc;
+}
+
+static int idxProcessTriggers(sqlite3expert *p, char **pzErr){
+ int rc = SQLITE_OK;
+ IdxWrite *pEnd = 0;
+ IdxWrite *pFirst = p->pWrite;
+
+ while( rc==SQLITE_OK && pFirst!=pEnd ){
+ IdxWrite *pIter;
+ for(pIter=pFirst; rc==SQLITE_OK && pIter!=pEnd; pIter=pIter->pNext){
+ rc = idxProcessOneTrigger(p, pIter, pzErr);
+ }
+ pEnd = pFirst;
+ pFirst = p->pWrite;
+ }
+
+ return rc;
+}
+
+/*
+** This function tests if the schema of the main database of database handle
+** db contains an object named zTab. Assuming no error occurs, output parameter
+** (*pbContains) is set to true if zTab exists, or false if it does not.
+**
+** Or, if an error occurs, an SQLite error code is returned. The final value
+** of (*pbContains) is undefined in this case.
+*/
+static int expertDbContainsObject(
+ sqlite3 *db,
+ const char *zTab,
+ int *pbContains /* OUT: True if object exists */
+){
+ const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?";
+ sqlite3_stmt *pSql = 0;
+ int rc = SQLITE_OK;
+ int ret = 0;
+
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_text(pSql, 1, zTab, -1, SQLITE_STATIC);
+ if( SQLITE_ROW==sqlite3_step(pSql) ){
+ ret = 1;
+ }
+ rc = sqlite3_finalize(pSql);
+ }
+
+ *pbContains = ret;
+ return rc;
+}
+
+/*
+** Execute SQL command zSql using database handle db. If no error occurs,
+** set (*pzErr) to NULL and return SQLITE_OK.
+**
+** If an error does occur, return an SQLite error code and set (*pzErr) to
+** point to a buffer containing an English language error message. Except,
+** if the error message begins with "no such module:", then ignore the
+** error and return as if the SQL statement had succeeded.
+**
+** This is used to copy as much of the database schema as possible while
+** ignoring any errors related to missing virtual table modules.
+*/
+static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){
+ int rc = SQLITE_OK;
+ char *zErr = 0;
+
+ rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
+ if( rc!=SQLITE_OK && zErr ){
+ int nErr = STRLEN(zErr);
+ if( nErr>=15 && memcmp(zErr, "no such module:", 15)==0 ){
+ sqlite3_free(zErr);
+ rc = SQLITE_OK;
+ zErr = 0;
+ }
+ }
+
+ *pzErr = zErr;
+ return rc;
+}
+
+static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
+ int rc = idxRegisterVtab(p);
+ sqlite3_stmt *pSchema = 0;
+
+ /* For each table in the main db schema:
+ **
+ ** 1) Add an entry to the p->pTable list, and
+ ** 2) Create the equivalent virtual table in dbv.
+ */
+ rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
+ "SELECT type, name, sql, 1, "
+ " substr(sql,1,14)=='create virtual' COLLATE nocase "
+ "FROM sqlite_schema "
+ "WHERE type IN ('table','view') AND "
+ " substr(name,1,7)!='sqlite_' COLLATE nocase "
+ " UNION ALL "
+ "SELECT type, name, sql, 2, 0 FROM sqlite_schema "
+ "WHERE type = 'trigger'"
+ " AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
+ "ORDER BY 4, 5 DESC, 1"
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
+ const char *zType = (const char*)sqlite3_column_text(pSchema, 0);
+ const char *zName = (const char*)sqlite3_column_text(pSchema, 1);
+ const char *zSql = (const char*)sqlite3_column_text(pSchema, 2);
+ int bVirtual = sqlite3_column_int(pSchema, 4);
+ int bExists = 0;
+
+ if( zType==0 || zName==0 ) continue;
+ rc = expertDbContainsObject(p->dbv, zName, &bExists);
+ if( rc || bExists ) continue;
+
+ if( zType[0]=='v' || zType[1]=='r' || bVirtual ){
+ /* A view. Or a trigger on a view. */
+ if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
+ }else{
+ IdxTable *pTab;
+ rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
+ if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){
+ int i;
+ char *zInner = 0;
+ char *zOuter = 0;
+ pTab->pNext = p->pTable;
+ p->pTable = pTab;
+
+ /* The statement the vtab will pass to sqlite3_declare_vtab() */
+ zInner = idxAppendText(&rc, 0, "CREATE TABLE x(");
+ for(i=0; inCol; i++){
+ zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s",
+ (i==0 ? "" : ", "), pTab->aCol[i].zName, pTab->aCol[i].zColl
+ );
+ }
+ zInner = idxAppendText(&rc, zInner, ")");
+
+ /* The CVT statement to create the vtab */
+ zOuter = idxAppendText(&rc, 0,
+ "CREATE VIRTUAL TABLE %Q USING expert(%Q)", zName, zInner
+ );
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(p->dbv, zOuter, 0, 0, pzErrmsg);
+ }
+ sqlite3_free(zInner);
+ sqlite3_free(zOuter);
+ }
+ }
+ }
+ idxFinalize(&rc, pSchema);
+ return rc;
+}
+
+struct IdxSampleCtx {
+ int iTarget;
+ double target; /* Target nRet/nRow value */
+ double nRow; /* Number of rows seen */
+ double nRet; /* Number of rows returned */
+};
+
+static void idxSampleFunc(
+ sqlite3_context *pCtx,
+ int argc,
+ sqlite3_value **argv
+){
+ struct IdxSampleCtx *p = (struct IdxSampleCtx*)sqlite3_user_data(pCtx);
+ int bRet;
+
+ (void)argv;
+ assert( argc==0 );
+ if( p->nRow==0.0 ){
+ bRet = 1;
+ }else{
+ bRet = (p->nRet / p->nRow) <= p->target;
+ if( bRet==0 ){
+ unsigned short rnd;
+ sqlite3_randomness(2, (void*)&rnd);
+ bRet = ((int)rnd % 100) <= p->iTarget;
+ }
+ }
+
+ sqlite3_result_int(pCtx, bRet);
+ p->nRow += 1.0;
+ p->nRet += (double)bRet;
+}
+
+struct IdxRemCtx {
+ int nSlot;
+ struct IdxRemSlot {
+ int eType; /* SQLITE_NULL, INTEGER, REAL, TEXT, BLOB */
+ i64 iVal; /* SQLITE_INTEGER value */
+ double rVal; /* SQLITE_FLOAT value */
+ int nByte; /* Bytes of space allocated at z */
+ int n; /* Size of buffer z */
+ char *z; /* SQLITE_TEXT/BLOB value */
+ } aSlot[1];
+};
+
+/*
+** Implementation of scalar function sqlite_expert_rem().
+*/
+static void idxRemFunc(
+ sqlite3_context *pCtx,
+ int argc,
+ sqlite3_value **argv
+){
+ struct IdxRemCtx *p = (struct IdxRemCtx*)sqlite3_user_data(pCtx);
+ struct IdxRemSlot *pSlot;
+ int iSlot;
+ assert( argc==2 );
+
+ iSlot = sqlite3_value_int(argv[0]);
+ assert( iSlotnSlot );
+ pSlot = &p->aSlot[iSlot];
+
+ switch( pSlot->eType ){
+ case SQLITE_NULL:
+ /* no-op */
+ break;
+
+ case SQLITE_INTEGER:
+ sqlite3_result_int64(pCtx, pSlot->iVal);
+ break;
+
+ case SQLITE_FLOAT:
+ sqlite3_result_double(pCtx, pSlot->rVal);
+ break;
+
+ case SQLITE_BLOB:
+ sqlite3_result_blob(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
+ break;
+
+ case SQLITE_TEXT:
+ sqlite3_result_text(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
+ break;
+ }
+
+ pSlot->eType = sqlite3_value_type(argv[1]);
+ switch( pSlot->eType ){
+ case SQLITE_NULL:
+ /* no-op */
+ break;
+
+ case SQLITE_INTEGER:
+ pSlot->iVal = sqlite3_value_int64(argv[1]);
+ break;
+
+ case SQLITE_FLOAT:
+ pSlot->rVal = sqlite3_value_double(argv[1]);
+ break;
+
+ case SQLITE_BLOB:
+ case SQLITE_TEXT: {
+ int nByte = sqlite3_value_bytes(argv[1]);
+ const void *pData = 0;
+ if( nByte>pSlot->nByte ){
+ char *zNew = (char*)sqlite3_realloc(pSlot->z, nByte*2);
+ if( zNew==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ pSlot->nByte = nByte*2;
+ pSlot->z = zNew;
+ }
+ pSlot->n = nByte;
+ if( pSlot->eType==SQLITE_BLOB ){
+ pData = sqlite3_value_blob(argv[1]);
+ if( pData ) memcpy(pSlot->z, pData, nByte);
+ }else{
+ pData = sqlite3_value_text(argv[1]);
+ memcpy(pSlot->z, pData, nByte);
+ }
+ break;
+ }
+ }
+}
+
+static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
+ int rc = SQLITE_OK;
+ const char *zMax =
+ "SELECT max(i.seqno) FROM "
+ " sqlite_schema AS s, "
+ " pragma_index_list(s.name) AS l, "
+ " pragma_index_info(l.name) AS i "
+ "WHERE s.type = 'table'";
+ sqlite3_stmt *pMax = 0;
+
+ *pnMax = 0;
+ rc = idxPrepareStmt(db, &pMax, pzErr, zMax);
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
+ *pnMax = sqlite3_column_int(pMax, 0) + 1;
+ }
+ idxFinalize(&rc, pMax);
+
+ return rc;
+}
+
+static int idxPopulateOneStat1(
+ sqlite3expert *p,
+ sqlite3_stmt *pIndexXInfo,
+ sqlite3_stmt *pWriteStat,
+ const char *zTab,
+ const char *zIdx,
+ char **pzErr
+){
+ char *zCols = 0;
+ char *zOrder = 0;
+ char *zQuery = 0;
+ int nCol = 0;
+ int i;
+ sqlite3_stmt *pQuery = 0;
+ int *aStat = 0;
+ int rc = SQLITE_OK;
+
+ assert( p->iSample>0 );
+
+ /* Formulate the query text */
+ sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
+ while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
+ const char *zComma = zCols==0 ? "" : ", ";
+ const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
+ const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
+ if( zName==0 ){
+ /* This index contains an expression. Ignore it. */
+ sqlite3_free(zCols);
+ sqlite3_free(zOrder);
+ return sqlite3_reset(pIndexXInfo);
+ }
+ zCols = idxAppendText(&rc, zCols,
+ "%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
+ zComma, zName, nCol, zName, zColl
+ );
+ zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
+ }
+ sqlite3_reset(pIndexXInfo);
+ if( rc==SQLITE_OK ){
+ if( p->iSample==100 ){
+ zQuery = sqlite3_mprintf(
+ "SELECT %s FROM %Q x ORDER BY %s", zCols, zTab, zOrder
+ );
+ }else{
+ zQuery = sqlite3_mprintf(
+ "SELECT %s FROM temp."UNIQUE_TABLE_NAME" x ORDER BY %s", zCols, zOrder
+ );
+ }
+ }
+ sqlite3_free(zCols);
+ sqlite3_free(zOrder);
+
+ /* Formulate the query text */
+ if( rc==SQLITE_OK ){
+ sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
+ rc = idxPrepareStmt(dbrem, &pQuery, pzErr, zQuery);
+ }
+ sqlite3_free(zQuery);
+
+ if( rc==SQLITE_OK ){
+ aStat = (int*)idxMalloc(&rc, sizeof(int)*(nCol+1));
+ }
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
+ IdxHashEntry *pEntry;
+ char *zStat = 0;
+ for(i=0; i<=nCol; i++) aStat[i] = 1;
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
+ aStat[0]++;
+ for(i=0; ihIdx, zIdx, STRLEN(zIdx));
+ if( pEntry ){
+ assert( pEntry->zVal2==0 );
+ pEntry->zVal2 = zStat;
+ }else{
+ sqlite3_free(zStat);
+ }
+ }
+ sqlite3_free(aStat);
+ idxFinalize(&rc, pQuery);
+
+ return rc;
+}
+
+static int idxBuildSampleTable(sqlite3expert *p, const char *zTab){
+ int rc;
+ char *zSql;
+
+ rc = sqlite3_exec(p->dbv,"DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ zSql = sqlite3_mprintf(
+ "CREATE TABLE temp." UNIQUE_TABLE_NAME " AS SELECT * FROM %Q", zTab
+ );
+ if( zSql==0 ) return SQLITE_NOMEM;
+ rc = sqlite3_exec(p->dbv, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+
+ return rc;
+}
+
+/*
+** This function is called as part of sqlite3_expert_analyze(). Candidate
+** indexes have already been created in database sqlite3expert.dbm, this
+** function populates sqlite_stat1 table in the same database.
+**
+** The stat1 data is generated by querying the
+*/
+static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
+ int rc = SQLITE_OK;
+ int nMax =0;
+ struct IdxRemCtx *pCtx = 0;
+ struct IdxSampleCtx samplectx;
+ int i;
+ i64 iPrev = -100000;
+ sqlite3_stmt *pAllIndex = 0;
+ sqlite3_stmt *pIndexXInfo = 0;
+ sqlite3_stmt *pWrite = 0;
+
+ const char *zAllIndex =
+ "SELECT s.rowid, s.name, l.name FROM "
+ " sqlite_schema AS s, "
+ " pragma_index_list(s.name) AS l "
+ "WHERE s.type = 'table'";
+ const char *zIndexXInfo =
+ "SELECT name, coll FROM pragma_index_xinfo(?) WHERE key";
+ const char *zWrite = "INSERT INTO sqlite_stat1 VALUES(?, ?, ?)";
+
+ /* If iSample==0, no sqlite_stat1 data is required. */
+ if( p->iSample==0 ) return SQLITE_OK;
+
+ rc = idxLargestIndex(p->dbm, &nMax, pzErr);
+ if( nMax<=0 || rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_exec(p->dbm, "ANALYZE; PRAGMA writable_schema=1", 0, 0, 0);
+
+ if( rc==SQLITE_OK ){
+ int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
+ pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
+ }
+
+ if( rc==SQLITE_OK ){
+ sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
+ rc = sqlite3_create_function(dbrem, "sqlite_expert_rem",
+ 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(p->db, "sqlite_expert_sample",
+ 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ pCtx->nSlot = nMax+1;
+ rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex);
+ }
+ if( rc==SQLITE_OK ){
+ rc = idxPrepareStmt(p->dbm, &pIndexXInfo, pzErr, zIndexXInfo);
+ }
+ if( rc==SQLITE_OK ){
+ rc = idxPrepareStmt(p->dbm, &pWrite, pzErr, zWrite);
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pAllIndex) ){
+ i64 iRowid = sqlite3_column_int64(pAllIndex, 0);
+ const char *zTab = (const char*)sqlite3_column_text(pAllIndex, 1);
+ const char *zIdx = (const char*)sqlite3_column_text(pAllIndex, 2);
+ if( zTab==0 || zIdx==0 ) continue;
+ if( p->iSample<100 && iPrev!=iRowid ){
+ samplectx.target = (double)p->iSample / 100.0;
+ samplectx.iTarget = p->iSample;
+ samplectx.nRow = 0.0;
+ samplectx.nRet = 0.0;
+ rc = idxBuildSampleTable(p, zTab);
+ if( rc!=SQLITE_OK ) break;
+ }
+ rc = idxPopulateOneStat1(p, pIndexXInfo, pWrite, zTab, zIdx, pzErr);
+ iPrev = iRowid;
+ }
+ if( rc==SQLITE_OK && p->iSample<100 ){
+ rc = sqlite3_exec(p->dbv,
+ "DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME, 0,0,0
+ );
+ }
+
+ idxFinalize(&rc, pAllIndex);
+ idxFinalize(&rc, pIndexXInfo);
+ idxFinalize(&rc, pWrite);
+
+ if( pCtx ){
+ for(i=0; inSlot; i++){
+ sqlite3_free(pCtx->aSlot[i].z);
+ }
+ sqlite3_free(pCtx);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
+ }
+
+ sqlite3_create_function(p->db, "sqlite_expert_rem", 2, SQLITE_UTF8, 0,0,0,0);
+ sqlite3_create_function(p->db, "sqlite_expert_sample", 0,SQLITE_UTF8,0,0,0,0);
+
+ sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
+ return rc;
+}
+
+/*
+** Define and possibly pretend to use a useless collation sequence.
+** This pretense allows expert to accept SQL using custom collations.
+*/
+int dummyCompare(void *up1, int up2, const void *up3, int up4, const void *up5){
+ (void)up1;
+ (void)up2;
+ (void)up3;
+ (void)up4;
+ (void)up5;
+ assert(0); /* VDBE should never be run. */
+ return 0;
+}
+/* And a callback to register above upon actual need */
+void useDummyCS(void *up1, sqlite3 *db, int etr, const char *zName){
+ (void)up1;
+ sqlite3_create_collation_v2(db, zName, etr, 0, dummyCompare, 0);
+}
+
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \
+ && !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
+/*
+** dummy functions for no-op implementation of UDFs during expert's work
+*/
+void dummyUDF(sqlite3_context *up1, int up2, sqlite3_value **up3){
+ (void)up1;
+ (void)up2;
+ (void)up3;
+ assert(0); /* VDBE should never be run. */
+}
+void dummyUDFvalue(sqlite3_context *up1){
+ (void)up1;
+ assert(0); /* VDBE should never be run. */
+}
+
+/*
+** Register UDFs from user database with another.
+*/
+int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){
+ sqlite3_stmt *pStmt;
+ int rc = sqlite3_prepare_v2(dbSrc,
+ "SELECT name,type,enc,narg,flags "
+ "FROM pragma_function_list() "
+ "WHERE builtin==0", -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
+ int nargs = sqlite3_column_int(pStmt,3);
+ int flags = sqlite3_column_int(pStmt,4);
+ const char *name = (char*)sqlite3_column_text(pStmt,0);
+ const char *type = (char*)sqlite3_column_text(pStmt,1);
+ const char *enc = (char*)sqlite3_column_text(pStmt,2);
+ if( name==0 || type==0 || enc==0 ){
+ /* no-op. Only happens on OOM */
+ }else{
+ int ienc = SQLITE_UTF8;
+ int rcf = SQLITE_ERROR;
+ if( strcmp(enc,"utf16le")==0 ) ienc = SQLITE_UTF16LE;
+ else if( strcmp(enc,"utf16be")==0 ) ienc = SQLITE_UTF16BE;
+ ienc |= (flags & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY));
+ if( strcmp(type,"w")==0 ){
+ rcf = sqlite3_create_window_function(dbDst,name,nargs,ienc,0,
+ dummyUDF,dummyUDFvalue,0,0,0);
+ }else if( strcmp(type,"a")==0 ){
+ rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0,
+ 0,dummyUDF,dummyUDFvalue);
+ }else if( strcmp(type,"s")==0 ){
+ rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0,
+ dummyUDF,0,0);
+ }
+ if( rcf!=SQLITE_OK ){
+ rc = rcf;
+ break;
+ }
+ }
+ }
+ sqlite3_finalize(pStmt);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ }
+ return rc;
+}
+#endif
+
+/*
+** Allocate a new sqlite3expert object.
+*/
+sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
+ int rc = SQLITE_OK;
+ sqlite3expert *pNew;
+
+ pNew = (sqlite3expert*)idxMalloc(&rc, sizeof(sqlite3expert));
+
+ /* Open two in-memory databases to work with. The "vtab database" (dbv)
+ ** will contain a virtual table corresponding to each real table in
+ ** the user database schema, and a copy of each view. It is used to
+ ** collect information regarding the WHERE, ORDER BY and other clauses
+ ** of the user's query.
+ */
+ if( rc==SQLITE_OK ){
+ pNew->db = db;
+ pNew->iSample = 100;
+ rc = sqlite3_open(":memory:", &pNew->dbv);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_open(":memory:", &pNew->dbm);
+ if( rc==SQLITE_OK ){
+ sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0);
+ }
+ }
+
+ /* Allow custom collations to be dealt with through prepare. */
+ if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbm,0,useDummyCS);
+ if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbv,0,useDummyCS);
+
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \
+ && !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
+ /* Register UDFs from database [db] with [dbm] and [dbv]. */
+ if( rc==SQLITE_OK ){
+ rc = registerUDFs(pNew->db, pNew->dbm);
+ }
+ if( rc==SQLITE_OK ){
+ rc = registerUDFs(pNew->db, pNew->dbv);
+ }
+#endif
+
+ /* Copy the entire schema of database [db] into [dbm]. */
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pSql = 0;
+ rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
+ "SELECT sql, name, substr(sql,1,14)=='create virtual' COLLATE nocase"
+ " FROM sqlite_schema WHERE substr(name,1,7)!='sqlite_' COLLATE nocase"
+ " ORDER BY 3 DESC, rowid"
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
+ const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
+ const char *zName = (const char*)sqlite3_column_text(pSql, 1);
+ int bExists = 0;
+ rc = expertDbContainsObject(pNew->dbm, zName, &bExists);
+ if( rc==SQLITE_OK && zSql && bExists==0 ){
+ rc = expertSchemaSql(pNew->dbm, zSql, pzErrmsg);
+ }
+ }
+ idxFinalize(&rc, pSql);
+ }
+
+ /* Create the vtab schema */
+ if( rc==SQLITE_OK ){
+ rc = idxCreateVtabSchema(pNew, pzErrmsg);
+ }
+
+ /* Register the auth callback with dbv */
+ if( rc==SQLITE_OK ){
+ sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
+ }
+
+ /* If an error has occurred, free the new object and reutrn NULL. Otherwise,
+ ** return the new sqlite3expert handle. */
+ if( rc!=SQLITE_OK ){
+ sqlite3_expert_destroy(pNew);
+ pNew = 0;
+ }
+ return pNew;
+}
+
+/*
+** Configure an sqlite3expert object.
+*/
+int sqlite3_expert_config(sqlite3expert *p, int op, ...){
+ int rc = SQLITE_OK;
+ va_list ap;
+ va_start(ap, op);
+ switch( op ){
+ case EXPERT_CONFIG_SAMPLE: {
+ int iVal = va_arg(ap, int);
+ if( iVal<0 ) iVal = 0;
+ if( iVal>100 ) iVal = 100;
+ p->iSample = iVal;
+ break;
+ }
+ default:
+ rc = SQLITE_NOTFOUND;
+ break;
+ }
+
+ va_end(ap);
+ return rc;
+}
+
+/*
+** Add an SQL statement to the analysis.
+*/
+int sqlite3_expert_sql(
+ sqlite3expert *p, /* From sqlite3_expert_new() */
+ const char *zSql, /* SQL statement to add */
+ char **pzErr /* OUT: Error message (if any) */
+){
+ IdxScan *pScanOrig = p->pScan;
+ IdxStatement *pStmtOrig = p->pStatement;
+ int rc = SQLITE_OK;
+ const char *zStmt = zSql;
+
+ if( p->bRun ) return SQLITE_MISUSE;
+
+ while( rc==SQLITE_OK && zStmt && zStmt[0] ){
+ sqlite3_stmt *pStmt = 0;
+ /* Ensure that the provided statement compiles against user's DB. */
+ rc = idxPrepareStmt(p->db, &pStmt, pzErr, zStmt);
+ if( rc!=SQLITE_OK ) break;
+ sqlite3_finalize(pStmt);
+ rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt);
+ if( rc==SQLITE_OK ){
+ if( pStmt ){
+ IdxStatement *pNew;
+ const char *z = sqlite3_sql(pStmt);
+ int n = STRLEN(z);
+ pNew = (IdxStatement*)idxMalloc(&rc, sizeof(IdxStatement) + n+1);
+ if( rc==SQLITE_OK ){
+ pNew->zSql = (char*)&pNew[1];
+ memcpy(pNew->zSql, z, n+1);
+ pNew->pNext = p->pStatement;
+ if( p->pStatement ) pNew->iId = p->pStatement->iId+1;
+ p->pStatement = pNew;
+ }
+ sqlite3_finalize(pStmt);
+ }
+ }else{
+ idxDatabaseError(p->dbv, pzErr);
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ idxScanFree(p->pScan, pScanOrig);
+ idxStatementFree(p->pStatement, pStmtOrig);
+ p->pScan = pScanOrig;
+ p->pStatement = pStmtOrig;
+ }
+
+ return rc;
+}
+
+int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){
+ int rc;
+ IdxHashEntry *pEntry;
+
+ /* Do trigger processing to collect any extra IdxScan structures */
+ rc = idxProcessTriggers(p, pzErr);
+
+ /* Create candidate indexes within the in-memory database file */
+ if( rc==SQLITE_OK ){
+ rc = idxCreateCandidates(p);
+ }else if ( rc==SQLITE_BUSY_TIMEOUT ){
+ if( pzErr )
+ *pzErr = sqlite3_mprintf("Cannot find a unique index name to propose.");
+ return rc;
+ }
+
+ /* Generate the stat1 data */
+ if( rc==SQLITE_OK ){
+ rc = idxPopulateStat1(p, pzErr);
+ }
+
+ /* Formulate the EXPERT_REPORT_CANDIDATES text */
+ for(pEntry=p->hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
+ p->zCandidates = idxAppendText(&rc, p->zCandidates,
+ "%s;%s%s\n", pEntry->zVal,
+ pEntry->zVal2 ? " -- stat1: " : "", pEntry->zVal2
+ );
+ }
+
+ /* Figure out which of the candidate indexes are preferred by the query
+ ** planner and report the results to the user. */
+ if( rc==SQLITE_OK ){
+ rc = idxFindIndexes(p, pzErr);
+ }
+
+ if( rc==SQLITE_OK ){
+ p->bRun = 1;
+ }
+ return rc;
+}
+
+/*
+** Return the total number of statements that have been added to this
+** sqlite3expert using sqlite3_expert_sql().
+*/
+int sqlite3_expert_count(sqlite3expert *p){
+ int nRet = 0;
+ if( p->pStatement ) nRet = p->pStatement->iId+1;
+ return nRet;
+}
+
+/*
+** Return a component of the report.
+*/
+const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){
+ const char *zRet = 0;
+ IdxStatement *pStmt;
+
+ if( p->bRun==0 ) return 0;
+ for(pStmt=p->pStatement; pStmt && pStmt->iId!=iStmt; pStmt=pStmt->pNext);
+ switch( eReport ){
+ case EXPERT_REPORT_SQL:
+ if( pStmt ) zRet = pStmt->zSql;
+ break;
+ case EXPERT_REPORT_INDEXES:
+ if( pStmt ) zRet = pStmt->zIdx;
+ break;
+ case EXPERT_REPORT_PLAN:
+ if( pStmt ) zRet = pStmt->zEQP;
+ break;
+ case EXPERT_REPORT_CANDIDATES:
+ zRet = p->zCandidates;
+ break;
+ }
+ return zRet;
+}
+
+/*
+** Free an sqlite3expert object.
+*/
+void sqlite3_expert_destroy(sqlite3expert *p){
+ if( p ){
+ sqlite3_close(p->dbm);
+ sqlite3_close(p->dbv);
+ idxScanFree(p->pScan, 0);
+ idxStatementFree(p->pStatement, 0);
+ idxTableFree(p->pTable);
+ idxWriteFree(p->pWrite);
+ idxHashClear(&p->hIdx);
+ sqlite3_free(p->zCandidates);
+ sqlite3_free(p);
+ }
+}
+
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
diff --git a/sqlite/ext/expert/sqlite3expert.h b/sqlite/ext/expert/sqlite3expert.h
new file mode 100644
index 00000000..60481372
--- /dev/null
+++ b/sqlite/ext/expert/sqlite3expert.h
@@ -0,0 +1,168 @@
+/*
+** 2017 April 07
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+#if !defined(SQLITEEXPERT_H)
+#define SQLITEEXPERT_H 1
+#include "sqlite3.h"
+
+typedef struct sqlite3expert sqlite3expert;
+
+/*
+** Create a new sqlite3expert object.
+**
+** If successful, a pointer to the new object is returned and (*pzErr) set
+** to NULL. Or, if an error occurs, NULL is returned and (*pzErr) set to
+** an English-language error message. In this case it is the responsibility
+** of the caller to eventually free the error message buffer using
+** sqlite3_free().
+*/
+sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErr);
+
+/*
+** Configure an sqlite3expert object.
+**
+** EXPERT_CONFIG_SAMPLE:
+** By default, sqlite3_expert_analyze() generates sqlite_stat1 data for
+** each candidate index. This involves scanning and sorting the entire
+** contents of each user database table once for each candidate index
+** associated with the table. For large databases, this can be
+** prohibitively slow. This option allows the sqlite3expert object to
+** be configured so that sqlite_stat1 data is instead generated based on a
+** subset of each table, or so that no sqlite_stat1 data is used at all.
+**
+** A single integer argument is passed to this option. If the value is less
+** than or equal to zero, then no sqlite_stat1 data is generated or used by
+** the analysis - indexes are recommended based on the database schema only.
+** Or, if the value is 100 or greater, complete sqlite_stat1 data is
+** generated for each candidate index (this is the default). Finally, if the
+** value falls between 0 and 100, then it represents the percentage of user
+** table rows that should be considered when generating sqlite_stat1 data.
+**
+** Examples:
+**
+** // Do not generate any sqlite_stat1 data
+** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 0);
+**
+** // Generate sqlite_stat1 data based on 10% of the rows in each table.
+** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 10);
+*/
+int sqlite3_expert_config(sqlite3expert *p, int op, ...);
+
+#define EXPERT_CONFIG_SAMPLE 1 /* int */
+
+/*
+** Specify zero or more SQL statements to be included in the analysis.
+**
+** Buffer zSql must contain zero or more complete SQL statements. This
+** function parses all statements contained in the buffer and adds them
+** to the internal list of statements to analyze. If successful, SQLITE_OK
+** is returned and (*pzErr) set to NULL. Or, if an error occurs - for example
+** due to a error in the SQL - an SQLite error code is returned and (*pzErr)
+** may be set to point to an English language error message. In this case
+** the caller is responsible for eventually freeing the error message buffer
+** using sqlite3_free().
+**
+** If an error does occur while processing one of the statements in the
+** buffer passed as the second argument, none of the statements in the
+** buffer are added to the analysis.
+**
+** This function must be called before sqlite3_expert_analyze(). If a call
+** to this function is made on an sqlite3expert object that has already
+** been passed to sqlite3_expert_analyze() SQLITE_MISUSE is returned
+** immediately and no statements are added to the analysis.
+*/
+int sqlite3_expert_sql(
+ sqlite3expert *p, /* From a successful sqlite3_expert_new() */
+ const char *zSql, /* SQL statement(s) to add */
+ char **pzErr /* OUT: Error message (if any) */
+);
+
+
+/*
+** This function is called after the sqlite3expert object has been configured
+** with all SQL statements using sqlite3_expert_sql() to actually perform
+** the analysis. Once this function has been called, it is not possible to
+** add further SQL statements to the analysis.
+**
+** If successful, SQLITE_OK is returned and (*pzErr) is set to NULL. Or, if
+** an error occurs, an SQLite error code is returned and (*pzErr) set to
+** point to a buffer containing an English language error message. In this
+** case it is the responsibility of the caller to eventually free the buffer
+** using sqlite3_free().
+**
+** If an error does occur within this function, the sqlite3expert object
+** is no longer useful for any purpose. At that point it is no longer
+** possible to add further SQL statements to the object or to re-attempt
+** the analysis. The sqlite3expert object must still be freed using a call
+** sqlite3_expert_destroy().
+*/
+int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr);
+
+/*
+** Return the total number of statements loaded using sqlite3_expert_sql().
+** The total number of SQL statements may be different from the total number
+** to calls to sqlite3_expert_sql().
+*/
+int sqlite3_expert_count(sqlite3expert*);
+
+/*
+** Return a component of the report.
+**
+** This function is called after sqlite3_expert_analyze() to extract the
+** results of the analysis. Each call to this function returns either a
+** NULL pointer or a pointer to a buffer containing a nul-terminated string.
+** The value passed as the third argument must be one of the EXPERT_REPORT_*
+** #define constants defined below.
+**
+** For some EXPERT_REPORT_* parameters, the buffer returned contains
+** information relating to a specific SQL statement. In these cases that
+** SQL statement is identified by the value passed as the second argument.
+** SQL statements are numbered from 0 in the order in which they are parsed.
+** If an out-of-range value (less than zero or equal to or greater than the
+** value returned by sqlite3_expert_count()) is passed as the second argument
+** along with such an EXPERT_REPORT_* parameter, NULL is always returned.
+**
+** EXPERT_REPORT_SQL:
+** Return the text of SQL statement iStmt.
+**
+** EXPERT_REPORT_INDEXES:
+** Return a buffer containing the CREATE INDEX statements for all recommended
+** indexes for statement iStmt. If there are no new recommeded indexes, NULL
+** is returned.
+**
+** EXPERT_REPORT_PLAN:
+** Return a buffer containing the EXPLAIN QUERY PLAN output for SQL query
+** iStmt after the proposed indexes have been added to the database schema.
+**
+** EXPERT_REPORT_CANDIDATES:
+** Return a pointer to a buffer containing the CREATE INDEX statements
+** for all indexes that were tested (for all SQL statements). The iStmt
+** parameter is ignored for EXPERT_REPORT_CANDIDATES calls.
+*/
+const char *sqlite3_expert_report(sqlite3expert*, int iStmt, int eReport);
+
+/*
+** Values for the third argument passed to sqlite3_expert_report().
+*/
+#define EXPERT_REPORT_SQL 1
+#define EXPERT_REPORT_INDEXES 2
+#define EXPERT_REPORT_PLAN 3
+#define EXPERT_REPORT_CANDIDATES 4
+
+/*
+** Free an (sqlite3expert*) handle and all associated resources. There
+** should be one call to this function for each successful call to
+** sqlite3-expert_new().
+*/
+void sqlite3_expert_destroy(sqlite3expert*);
+
+#endif /* !defined(SQLITEEXPERT_H) */
diff --git a/sqlite/ext/expert/test_expert.c b/sqlite/ext/expert/test_expert.c
new file mode 100644
index 00000000..cae5d0f2
--- /dev/null
+++ b/sqlite/ext/expert/test_expert.c
@@ -0,0 +1,212 @@
+/*
+** 2017 April 07
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+
+#if defined(SQLITE_TEST)
+
+#include "sqlite3expert.h"
+#include
+#include
+#include "tclsqlite.h"
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/*
+** Extract an sqlite3* db handle from the object passed as the second
+** argument. If successful, set *pDb to point to the db handle and return
+** TCL_OK. Otherwise, return TCL_ERROR.
+*/
+static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
+ Tcl_CmdInfo info;
+ if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
+ Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
+ return TCL_ERROR;
+ }
+
+ *pDb = *(sqlite3 **)info.objClientData;
+ return TCL_OK;
+}
+
+
+/*
+** Tclcmd: $expert sql SQL
+** $expert analyze
+** $expert count
+** $expert report STMT EREPORT
+** $expert destroy
+*/
+static int SQLITE_TCLAPI testExpertCmd(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3expert *pExpert = (sqlite3expert*)clientData;
+ struct Subcmd {
+ const char *zSub;
+ int nArg;
+ const char *zMsg;
+ } aSub[] = {
+ { "sql", 1, "TABLE", }, /* 0 */
+ { "analyze", 0, "", }, /* 1 */
+ { "count", 0, "", }, /* 2 */
+ { "report", 2, "STMT EREPORT", }, /* 3 */
+ { "destroy", 0, "", }, /* 4 */
+ { 0 }
+ };
+ int iSub;
+ int rc = TCL_OK;
+ char *zErr = 0;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
+ return TCL_ERROR;
+ }
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
+ );
+ if( rc!=TCL_OK ) return rc;
+ if( objc!=2+aSub[iSub].nArg ){
+ Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
+ return TCL_ERROR;
+ }
+
+ switch( iSub ){
+ case 0: { /* sql */
+ char *zArg = Tcl_GetString(objv[2]);
+ rc = sqlite3_expert_sql(pExpert, zArg, &zErr);
+ break;
+ }
+
+ case 1: { /* analyze */
+ rc = sqlite3_expert_analyze(pExpert, &zErr);
+ break;
+ }
+
+ case 2: { /* count */
+ int n = sqlite3_expert_count(pExpert);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
+ break;
+ }
+
+ case 3: { /* report */
+ const char *aEnum[] = {
+ "sql", "indexes", "plan", "candidates", 0
+ };
+ int iEnum;
+ int iStmt;
+ const char *zReport;
+
+ if( Tcl_GetIntFromObj(interp, objv[2], &iStmt)
+ || Tcl_GetIndexFromObj(interp, objv[3], aEnum, "report", 0, &iEnum)
+ ){
+ return TCL_ERROR;
+ }
+
+ assert( EXPERT_REPORT_SQL==1 );
+ assert( EXPERT_REPORT_INDEXES==2 );
+ assert( EXPERT_REPORT_PLAN==3 );
+ assert( EXPERT_REPORT_CANDIDATES==4 );
+ zReport = sqlite3_expert_report(pExpert, iStmt, 1+iEnum);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zReport, -1));
+ break;
+ }
+
+ default: /* destroy */
+ assert( iSub==4 );
+ Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
+ break;
+ }
+
+ if( rc!=TCL_OK ){
+ if( zErr ){
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
+ }else{
+ extern const char *sqlite3ErrName(int);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
+ }
+ }
+ sqlite3_free(zErr);
+ return rc;
+}
+
+static void SQLITE_TCLAPI testExpertDel(void *clientData){
+ sqlite3expert *pExpert = (sqlite3expert*)clientData;
+ sqlite3_expert_destroy(pExpert);
+}
+
+/*
+** sqlite3_expert_new DB
+*/
+static int SQLITE_TCLAPI test_sqlite3_expert_new(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static int iCmd = 0;
+ sqlite3 *db;
+ char *zCmd = 0;
+ char *zErr = 0;
+ sqlite3expert *pExpert;
+ int rc = TCL_OK;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( dbHandleFromObj(interp, objv[1], &db) ){
+ return TCL_ERROR;
+ }
+
+ zCmd = sqlite3_mprintf("sqlite3expert%d", ++iCmd);
+ if( zCmd==0 ){
+ Tcl_AppendResult(interp, "out of memory", (char*)0);
+ return TCL_ERROR;
+ }
+
+ pExpert = sqlite3_expert_new(db, &zErr);
+ if( pExpert==0 ){
+ Tcl_AppendResult(interp, zErr, (char*)0);
+ rc = TCL_ERROR;
+ }else{
+ void *p = (void*)pExpert;
+ Tcl_CreateObjCommand(interp, zCmd, testExpertCmd, p, testExpertDel);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
+ }
+
+ sqlite3_free(zCmd);
+ sqlite3_free(zErr);
+ return rc;
+}
+
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+
+int TestExpert_Init(Tcl_Interp *interp){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ struct Cmd {
+ const char *zCmd;
+ Tcl_ObjCmdProc *xProc;
+ } aCmd[] = {
+ { "sqlite3_expert_new", test_sqlite3_expert_new },
+ };
+ int i;
+
+ for(i=0; izCmd, p->xProc, 0, 0);
+ }
+#endif
+ return TCL_OK;
+}
+
+#endif
diff --git a/sqlite/ext/fts1/README.txt b/sqlite/ext/fts1/README.txt
deleted file mode 100644
index 292b7daa..00000000
--- a/sqlite/ext/fts1/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This folder contains source code to the first full-text search
-extension for SQLite.
diff --git a/sqlite/ext/fts1/ft_hash.c b/sqlite/ext/fts1/ft_hash.c
deleted file mode 100644
index 8b3a7064..00000000
--- a/sqlite/ext/fts1/ft_hash.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the implementation of generic hash-tables used in SQLite.
-** We've modified it slightly to serve as a standalone hash table
-** implementation for the full-text indexing module.
-*/
-#include
-#include
-#include
-
-#include "ft_hash.h"
-
-void *malloc_and_zero(int n){
- void *p = malloc(n);
- if( p ){
- memset(p, 0, n);
- }
- return p;
-}
-
-/* Turn bulk memory into a hash table object by initializing the
-** fields of the Hash structure.
-**
-** "pNew" is a pointer to the hash table that is to be initialized.
-** keyClass is one of the constants HASH_INT, HASH_POINTER,
-** HASH_BINARY, or HASH_STRING. The value of keyClass
-** determines what kind of key the hash table will use. "copyKey" is
-** true if the hash table should make its own private copy of keys and
-** false if it should just use the supplied pointer. CopyKey only makes
-** sense for HASH_STRING and HASH_BINARY and is ignored
-** for other key classes.
-*/
-void HashInit(Hash *pNew, int keyClass, int copyKey){
- assert( pNew!=0 );
- assert( keyClass>=HASH_STRING && keyClass<=HASH_BINARY );
- pNew->keyClass = keyClass;
-#if 0
- if( keyClass==HASH_POINTER || keyClass==HASH_INT ) copyKey = 0;
-#endif
- pNew->copyKey = copyKey;
- pNew->first = 0;
- pNew->count = 0;
- pNew->htsize = 0;
- pNew->ht = 0;
- pNew->xMalloc = malloc_and_zero;
- pNew->xFree = free;
-}
-
-/* Remove all entries from a hash table. Reclaim all memory.
-** Call this routine to delete a hash table or to reset a hash table
-** to the empty state.
-*/
-void HashClear(Hash *pH){
- HashElem *elem; /* For looping over all elements of the table */
-
- assert( pH!=0 );
- elem = pH->first;
- pH->first = 0;
- if( pH->ht ) pH->xFree(pH->ht);
- pH->ht = 0;
- pH->htsize = 0;
- while( elem ){
- HashElem *next_elem = elem->next;
- if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
- }
- pH->xFree(elem);
- elem = next_elem;
- }
- pH->count = 0;
-}
-
-#if 0 /* NOT USED */
-/*
-** Hash and comparison functions when the mode is HASH_INT
-*/
-static int intHash(const void *pKey, int nKey){
- return nKey ^ (nKey<<8) ^ (nKey>>8);
-}
-static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- return n2 - n1;
-}
-#endif
-
-#if 0 /* NOT USED */
-/*
-** Hash and comparison functions when the mode is HASH_POINTER
-*/
-static int ptrHash(const void *pKey, int nKey){
- uptr x = Addr(pKey);
- return x ^ (x<<8) ^ (x>>8);
-}
-static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( pKey1==pKey2 ) return 0;
- if( pKey1 0 ){
- h = (h<<3) ^ h ^ *z++;
- nKey--;
- }
- return h & 0x7fffffff;
-}
-static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( n1!=n2 ) return 1;
- return strncmp((const char*)pKey1,(const char*)pKey2,n1);
-}
-
-/*
-** Hash and comparison functions when the mode is HASH_BINARY
-*/
-static int binHash(const void *pKey, int nKey){
- int h = 0;
- const char *z = (const char *)pKey;
- while( nKey-- > 0 ){
- h = (h<<3) ^ h ^ *(z++);
- }
- return h & 0x7fffffff;
-}
-static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( n1!=n2 ) return 1;
- return memcmp(pKey1,pKey2,n1);
-}
-
-/*
-** Return a pointer to the appropriate hash function given the key class.
-**
-** The C syntax in this function definition may be unfamilar to some
-** programmers, so we provide the following additional explanation:
-**
-** The name of the function is "hashFunction". The function takes a
-** single parameter "keyClass". The return value of hashFunction()
-** is a pointer to another function. Specifically, the return value
-** of hashFunction() is a pointer to a function that takes two parameters
-** with types "const void*" and "int" and returns an "int".
-*/
-static int (*hashFunction(int keyClass))(const void*,int){
-#if 0 /* HASH_INT and HASH_POINTER are never used */
- switch( keyClass ){
- case HASH_INT: return &intHash;
- case HASH_POINTER: return &ptrHash;
- case HASH_STRING: return &strHash;
- case HASH_BINARY: return &binHash;;
- default: break;
- }
- return 0;
-#else
- if( keyClass==HASH_STRING ){
- return &strHash;
- }else{
- assert( keyClass==HASH_BINARY );
- return &binHash;
- }
-#endif
-}
-
-/*
-** Return a pointer to the appropriate hash function given the key class.
-**
-** For help in interpreted the obscure C code in the function definition,
-** see the header comment on the previous function.
-*/
-static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
-#if 0 /* HASH_INT and HASH_POINTER are never used */
- switch( keyClass ){
- case HASH_INT: return &intCompare;
- case HASH_POINTER: return &ptrCompare;
- case HASH_STRING: return &strCompare;
- case HASH_BINARY: return &binCompare;
- default: break;
- }
- return 0;
-#else
- if( keyClass==HASH_STRING ){
- return &strCompare;
- }else{
- assert( keyClass==HASH_BINARY );
- return &binCompare;
- }
-#endif
-}
-
-/* Link an element into the hash table
-*/
-static void insertElement(
- Hash *pH, /* The complete hash table */
- struct _ht *pEntry, /* The entry into which pNew is inserted */
- HashElem *pNew /* The element to be inserted */
-){
- HashElem *pHead; /* First element already in pEntry */
- pHead = pEntry->chain;
- if( pHead ){
- pNew->next = pHead;
- pNew->prev = pHead->prev;
- if( pHead->prev ){ pHead->prev->next = pNew; }
- else { pH->first = pNew; }
- pHead->prev = pNew;
- }else{
- pNew->next = pH->first;
- if( pH->first ){ pH->first->prev = pNew; }
- pNew->prev = 0;
- pH->first = pNew;
- }
- pEntry->count++;
- pEntry->chain = pNew;
-}
-
-
-/* Resize the hash table so that it cantains "new_size" buckets.
-** "new_size" must be a power of 2. The hash table might fail
-** to resize if sqliteMalloc() fails.
-*/
-static void rehash(Hash *pH, int new_size){
- struct _ht *new_ht; /* The new hash table */
- HashElem *elem, *next_elem; /* For looping over existing elements */
- int (*xHash)(const void*,int); /* The hash function */
-
- assert( (new_size & (new_size-1))==0 );
- new_ht = (struct _ht *)pH->xMalloc( new_size*sizeof(struct _ht) );
- if( new_ht==0 ) return;
- if( pH->ht ) pH->xFree(pH->ht);
- pH->ht = new_ht;
- pH->htsize = new_size;
- xHash = hashFunction(pH->keyClass);
- for(elem=pH->first, pH->first=0; elem; elem = next_elem){
- int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
- next_elem = elem->next;
- insertElement(pH, &new_ht[h], elem);
- }
-}
-
-/* This function (for internal use only) locates an element in an
-** hash table that matches the given key. The hash for this key has
-** already been computed and is passed as the 4th parameter.
-*/
-static HashElem *findElementGivenHash(
- const Hash *pH, /* The pH to be searched */
- const void *pKey, /* The key we are searching for */
- int nKey,
- int h /* The hash for this key. */
-){
- HashElem *elem; /* Used to loop thru the element list */
- int count; /* Number of elements left to test */
- int (*xCompare)(const void*,int,const void*,int); /* comparison function */
-
- if( pH->ht ){
- struct _ht *pEntry = &pH->ht[h];
- elem = pEntry->chain;
- count = pEntry->count;
- xCompare = compareFunction(pH->keyClass);
- while( count-- && elem ){
- if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
- return elem;
- }
- elem = elem->next;
- }
- }
- return 0;
-}
-
-/* Remove a single entry from the hash table given a pointer to that
-** element and a hash on the element's key.
-*/
-static void removeElementGivenHash(
- Hash *pH, /* The pH containing "elem" */
- HashElem* elem, /* The element to be removed from the pH */
- int h /* Hash value for the element */
-){
- struct _ht *pEntry;
- if( elem->prev ){
- elem->prev->next = elem->next;
- }else{
- pH->first = elem->next;
- }
- if( elem->next ){
- elem->next->prev = elem->prev;
- }
- pEntry = &pH->ht[h];
- if( pEntry->chain==elem ){
- pEntry->chain = elem->next;
- }
- pEntry->count--;
- if( pEntry->count<=0 ){
- pEntry->chain = 0;
- }
- if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
- }
- pH->xFree( elem );
- pH->count--;
- if( pH->count<=0 ){
- assert( pH->first==0 );
- assert( pH->count==0 );
- HashClear(pH);
- }
-}
-
-/* Attempt to locate an element of the hash table pH with a key
-** that matches pKey,nKey. Return the data for this element if it is
-** found, or NULL if there is no match.
-*/
-void *HashFind(const Hash *pH, const void *pKey, int nKey){
- int h; /* A hash on key */
- HashElem *elem; /* The element that matches key */
- int (*xHash)(const void*,int); /* The hash function */
-
- if( pH==0 || pH->ht==0 ) return 0;
- xHash = hashFunction(pH->keyClass);
- assert( xHash!=0 );
- h = (*xHash)(pKey,nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
- return elem ? elem->data : 0;
-}
-
-/* Insert an element into the hash table pH. The key is pKey,nKey
-** and the data is "data".
-**
-** If no element exists with a matching key, then a new
-** element is created. A copy of the key is made if the copyKey
-** flag is set. NULL is returned.
-**
-** If another element already exists with the same key, then the
-** new data replaces the old data and the old data is returned.
-** The key is not copied in this instance. If a malloc fails, then
-** the new data is returned and the hash table is unchanged.
-**
-** If the "data" parameter to this function is NULL, then the
-** element corresponding to "key" is removed from the hash table.
-*/
-void *HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
- int hraw; /* Raw hash value of the key */
- int h; /* the hash of the key modulo hash table size */
- HashElem *elem; /* Used to loop thru the element list */
- HashElem *new_elem; /* New element added to the pH */
- int (*xHash)(const void*,int); /* The hash function */
-
- assert( pH!=0 );
- xHash = hashFunction(pH->keyClass);
- assert( xHash!=0 );
- hraw = (*xHash)(pKey, nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- elem = findElementGivenHash(pH,pKey,nKey,h);
- if( elem ){
- void *old_data = elem->data;
- if( data==0 ){
- removeElementGivenHash(pH,elem,h);
- }else{
- elem->data = data;
- }
- return old_data;
- }
- if( data==0 ) return 0;
- new_elem = (HashElem*)pH->xMalloc( sizeof(HashElem) );
- if( new_elem==0 ) return data;
- if( pH->copyKey && pKey!=0 ){
- new_elem->pKey = pH->xMalloc( nKey );
- if( new_elem->pKey==0 ){
- pH->xFree(new_elem);
- return data;
- }
- memcpy((void*)new_elem->pKey, pKey, nKey);
- }else{
- new_elem->pKey = (void*)pKey;
- }
- new_elem->nKey = nKey;
- pH->count++;
- if( pH->htsize==0 ){
- rehash(pH,8);
- if( pH->htsize==0 ){
- pH->count = 0;
- pH->xFree(new_elem);
- return data;
- }
- }
- if( pH->count > pH->htsize ){
- rehash(pH,pH->htsize*2);
- }
- assert( pH->htsize>0 );
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- insertElement(pH, &pH->ht[h], new_elem);
- new_elem->data = data;
- return 0;
-}
diff --git a/sqlite/ext/fts1/ft_hash.h b/sqlite/ext/fts1/ft_hash.h
deleted file mode 100644
index 95871a45..00000000
--- a/sqlite/ext/fts1/ft_hash.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the header file for the generic hash-table implementation
-** used in SQLite. We've modified it slightly to serve as a standalone
-** hash table implementation for the full-text indexing module.
-**
-*/
-#ifndef _HASH_H_
-#define _HASH_H_
-
-/* Forward declarations of structures. */
-typedef struct Hash Hash;
-typedef struct HashElem HashElem;
-
-/* A complete hash table is an instance of the following structure.
-** The internals of this structure are intended to be opaque -- client
-** code should not attempt to access or modify the fields of this structure
-** directly. Change this structure only by using the routines below.
-** However, many of the "procedures" and "functions" for modifying and
-** accessing this structure are really macros, so we can't really make
-** this structure opaque.
-*/
-struct Hash {
- char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
- char copyKey; /* True if copy of key made on insert */
- int count; /* Number of entries in this table */
- HashElem *first; /* The first element of the array */
- void *(*xMalloc)(int); /* malloc() function to use */
- void (*xFree)(void *); /* free() function to use */
- int htsize; /* Number of buckets in the hash table */
- struct _ht { /* the hash table */
- int count; /* Number of entries with this hash */
- HashElem *chain; /* Pointer to first entry with this hash */
- } *ht;
-};
-
-/* Each element in the hash table is an instance of the following
-** structure. All elements are stored on a single doubly-linked list.
-**
-** Again, this structure is intended to be opaque, but it can't really
-** be opaque because it is used by macros.
-*/
-struct HashElem {
- HashElem *next, *prev; /* Next and previous elements in the table */
- void *data; /* Data associated with this element */
- void *pKey; int nKey; /* Key associated with this element */
-};
-
-/*
-** There are 4 different modes of operation for a hash table:
-**
-** HASH_INT nKey is used as the key and pKey is ignored.
-**
-** HASH_POINTER pKey is used as the key and nKey is ignored.
-**
-** HASH_STRING pKey points to a string that is nKey bytes long
-** (including the null-terminator, if any). Case
-** is respected in comparisons.
-**
-** HASH_BINARY pKey points to binary data nKey bytes long.
-** memcmp() is used to compare keys.
-**
-** A copy of the key is made for HASH_STRING and HASH_BINARY
-** if the copyKey parameter to HashInit is 1.
-*/
-/* #define HASH_INT 1 // NOT USED */
-/* #define HASH_POINTER 2 // NOT USED */
-#define HASH_STRING 3
-#define HASH_BINARY 4
-
-/*
-** Access routines. To delete, insert a NULL pointer.
-*/
-void HashInit(Hash*, int keytype, int copyKey);
-void *HashInsert(Hash*, const void *pKey, int nKey, void *pData);
-void *HashFind(const Hash*, const void *pKey, int nKey);
-void HashClear(Hash*);
-
-/*
-** Macros for looping over all elements of a hash table. The idiom is
-** like this:
-**
-** Hash h;
-** HashElem *p;
-** ...
-** for(p=HashFirst(&h); p; p=HashNext(p)){
-** SomeStructure *pData = HashData(p);
-** // do something with pData
-** }
-*/
-#define HashFirst(H) ((H)->first)
-#define HashNext(E) ((E)->next)
-#define HashData(E) ((E)->data)
-#define HashKey(E) ((E)->pKey)
-#define HashKeysize(E) ((E)->nKey)
-
-/*
-** Number of entries in a hash table
-*/
-#define HashCount(H) ((H)->count)
-
-#endif /* _HASH_H_ */
diff --git a/sqlite/ext/fts1/fts1.c b/sqlite/ext/fts1/fts1.c
deleted file mode 100644
index 77fa9e23..00000000
--- a/sqlite/ext/fts1/fts1.c
+++ /dev/null
@@ -1,3348 +0,0 @@
-/* fts1 has a design flaw which can lead to database corruption (see
-** below). It is recommended not to use it any longer, instead use
-** fts3 (or higher). If you believe that your use of fts1 is safe,
-** add -DSQLITE_ENABLE_BROKEN_FTS1=1 to your CFLAGS.
-*/
-#if (!defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)) \
- && !defined(SQLITE_ENABLE_BROKEN_FTS1)
-#error fts1 has a design flaw and has been deprecated.
-#endif
-/* The flaw is that fts1 uses the content table's unaliased rowid as
-** the unique docid. fts1 embeds the rowid in the index it builds,
-** and expects the rowid to not change. The SQLite VACUUM operation
-** will renumber such rowids, thereby breaking fts1. If you are using
-** fts1 in a system which has disabled VACUUM, then you can continue
-** to use it safely. Note that PRAGMA auto_vacuum does NOT disable
-** VACUUM, though systems using auto_vacuum are unlikely to invoke
-** VACUUM.
-**
-** fts1 should be safe even across VACUUM if you only insert documents
-** and never delete.
-*/
-
-/* The author disclaims copyright to this source code.
- *
- * This is an SQLite module implementing full-text search.
- */
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS1 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS1 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
-
-#if defined(SQLITE_ENABLE_FTS1) && !defined(SQLITE_CORE)
-# define SQLITE_CORE 1
-#endif
-
-#include
-#include
-#include
-#include
-#include
-
-#include "fts1.h"
-#include "fts1_hash.h"
-#include "fts1_tokenizer.h"
-#include "sqlite3.h"
-#include "sqlite3ext.h"
-SQLITE_EXTENSION_INIT1
-
-
-#if 0
-# define TRACE(A) printf A; fflush(stdout)
-#else
-# define TRACE(A)
-#endif
-
-/* utility functions */
-
-typedef struct StringBuffer {
- int len; /* length, not including null terminator */
- int alloced; /* Space allocated for s[] */
- char *s; /* Content of the string */
-} StringBuffer;
-
-static void initStringBuffer(StringBuffer *sb){
- sb->len = 0;
- sb->alloced = 100;
- sb->s = malloc(100);
- sb->s[0] = '\0';
-}
-
-static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
- if( sb->len + nFrom >= sb->alloced ){
- sb->alloced = sb->len + nFrom + 100;
- sb->s = realloc(sb->s, sb->alloced+1);
- if( sb->s==0 ){
- initStringBuffer(sb);
- return;
- }
- }
- memcpy(sb->s + sb->len, zFrom, nFrom);
- sb->len += nFrom;
- sb->s[sb->len] = 0;
-}
-static void append(StringBuffer *sb, const char *zFrom){
- nappend(sb, zFrom, strlen(zFrom));
-}
-
-/* We encode variable-length integers in little-endian order using seven bits
- * per byte as follows:
-**
-** KEY:
-** A = 0xxxxxxx 7 bits of data and one flag bit
-** B = 1xxxxxxx 7 bits of data and one flag bit
-**
-** 7 bits - A
-** 14 bits - BA
-** 21 bits - BBA
-** and so on.
-*/
-
-/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
-#define VARINT_MAX 10
-
-/* Write a 64-bit variable-length integer to memory starting at p[0].
- * The length of data written will be between 1 and VARINT_MAX bytes.
- * The number of bytes written is returned. */
-static int putVarint(char *p, sqlite_int64 v){
- unsigned char *q = (unsigned char *) p;
- sqlite_uint64 vu = v;
- do{
- *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
- vu >>= 7;
- }while( vu!=0 );
- q[-1] &= 0x7f; /* turn off high bit in final byte */
- assert( q - (unsigned char *)p <= VARINT_MAX );
- return (int) (q - (unsigned char *)p);
-}
-
-/* Read a 64-bit variable-length integer from memory starting at p[0].
- * Return the number of bytes read, or 0 on error.
- * The value is stored in *v. */
-static int getVarint(const char *p, sqlite_int64 *v){
- const unsigned char *q = (const unsigned char *) p;
- sqlite_uint64 x = 0, y = 1;
- while( (*q & 0x80) == 0x80 ){
- x += y * (*q++ & 0x7f);
- y <<= 7;
- if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */
- assert( 0 );
- return 0;
- }
- }
- x += y * (*q++);
- *v = (sqlite_int64) x;
- return (int) (q - (unsigned char *)p);
-}
-
-static int getVarint32(const char *p, int *pi){
- sqlite_int64 i;
- int ret = getVarint(p, &i);
- *pi = (int) i;
- assert( *pi==i );
- return ret;
-}
-
-/*** Document lists ***
- *
- * A document list holds a sorted list of varint-encoded document IDs.
- *
- * A doclist with type DL_POSITIONS_OFFSETS is stored like this:
- *
- * array {
- * varint docid;
- * array {
- * varint position; (delta from previous position plus POS_BASE)
- * varint startOffset; (delta from previous startOffset)
- * varint endOffset; (delta from startOffset)
- * }
- * }
- *
- * Here, array { X } means zero or more occurrences of X, adjacent in memory.
- *
- * A position list may hold positions for text in multiple columns. A position
- * POS_COLUMN is followed by a varint containing the index of the column for
- * following positions in the list. Any positions appearing before any
- * occurrences of POS_COLUMN are for column 0.
- *
- * A doclist with type DL_POSITIONS is like the above, but holds only docids
- * and positions without offset information.
- *
- * A doclist with type DL_DOCIDS is like the above, but holds only docids
- * without positions or offset information.
- *
- * On disk, every document list has positions and offsets, so we don't bother
- * to serialize a doclist's type.
- *
- * We don't yet delta-encode document IDs; doing so will probably be a
- * modest win.
- *
- * NOTE(shess) I've thought of a slightly (1%) better offset encoding.
- * After the first offset, estimate the next offset by using the
- * current token position and the previous token position and offset,
- * offset to handle some variance. So the estimate would be
- * (iPosition*w->iStartOffset/w->iPosition-64), which is delta-encoded
- * as normal. Offsets more than 64 chars from the estimate are
- * encoded as the delta to the previous start offset + 128. An
- * additional tiny increment can be gained by using the end offset of
- * the previous token to make the estimate a tiny bit more precise.
-*/
-
-/* It is not safe to call isspace(), tolower(), or isalnum() on
-** hi-bit-set characters. This is the same solution used in the
-** tokenizer.
-*/
-/* TODO(shess) The snippet-generation code should be using the
-** tokenizer-generated tokens rather than doing its own local
-** tokenization.
-*/
-/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
-static int safe_isspace(char c){
- return (c&0x80)==0 ? isspace((unsigned char)c) : 0;
-}
-static int safe_tolower(char c){
- return (c&0x80)==0 ? tolower((unsigned char)c) : c;
-}
-static int safe_isalnum(char c){
- return (c&0x80)==0 ? isalnum((unsigned char)c) : 0;
-}
-
-typedef enum DocListType {
- DL_DOCIDS, /* docids only */
- DL_POSITIONS, /* docids + positions */
- DL_POSITIONS_OFFSETS /* docids + positions + offsets */
-} DocListType;
-
-/*
-** By default, only positions and not offsets are stored in the doclists.
-** To change this so that offsets are stored too, compile with
-**
-** -DDL_DEFAULT=DL_POSITIONS_OFFSETS
-**
-*/
-#ifndef DL_DEFAULT
-# define DL_DEFAULT DL_POSITIONS
-#endif
-
-typedef struct DocList {
- char *pData;
- int nData;
- DocListType iType;
- int iLastColumn; /* the last column written */
- int iLastPos; /* the last position written */
- int iLastOffset; /* the last start offset written */
-} DocList;
-
-enum {
- POS_END = 0, /* end of this position list */
- POS_COLUMN, /* followed by new column number */
- POS_BASE
-};
-
-/* Initialize a new DocList to hold the given data. */
-static void docListInit(DocList *d, DocListType iType,
- const char *pData, int nData){
- d->nData = nData;
- if( nData>0 ){
- d->pData = malloc(nData);
- memcpy(d->pData, pData, nData);
- } else {
- d->pData = NULL;
- }
- d->iType = iType;
- d->iLastColumn = 0;
- d->iLastPos = d->iLastOffset = 0;
-}
-
-/* Create a new dynamically-allocated DocList. */
-static DocList *docListNew(DocListType iType){
- DocList *d = (DocList *) malloc(sizeof(DocList));
- docListInit(d, iType, 0, 0);
- return d;
-}
-
-static void docListDestroy(DocList *d){
- free(d->pData);
-#ifndef NDEBUG
- memset(d, 0x55, sizeof(*d));
-#endif
-}
-
-static void docListDelete(DocList *d){
- docListDestroy(d);
- free(d);
-}
-
-static char *docListEnd(DocList *d){
- return d->pData + d->nData;
-}
-
-/* Append a varint to a DocList's data. */
-static void appendVarint(DocList *d, sqlite_int64 i){
- char c[VARINT_MAX];
- int n = putVarint(c, i);
- d->pData = realloc(d->pData, d->nData + n);
- memcpy(d->pData + d->nData, c, n);
- d->nData += n;
-}
-
-static void docListAddDocid(DocList *d, sqlite_int64 iDocid){
- appendVarint(d, iDocid);
- if( d->iType>=DL_POSITIONS ){
- appendVarint(d, POS_END); /* initially empty position list */
- d->iLastColumn = 0;
- d->iLastPos = d->iLastOffset = 0;
- }
-}
-
-/* helper function for docListAddPos and docListAddPosOffset */
-static void addPos(DocList *d, int iColumn, int iPos){
- assert( d->nData>0 );
- --d->nData; /* remove previous terminator */
- if( iColumn!=d->iLastColumn ){
- assert( iColumn>d->iLastColumn );
- appendVarint(d, POS_COLUMN);
- appendVarint(d, iColumn);
- d->iLastColumn = iColumn;
- d->iLastPos = d->iLastOffset = 0;
- }
- assert( iPos>=d->iLastPos );
- appendVarint(d, iPos-d->iLastPos+POS_BASE);
- d->iLastPos = iPos;
-}
-
-/* Add a position to the last position list in a doclist. */
-static void docListAddPos(DocList *d, int iColumn, int iPos){
- assert( d->iType==DL_POSITIONS );
- addPos(d, iColumn, iPos);
- appendVarint(d, POS_END); /* add new terminator */
-}
-
-/*
-** Add a position and starting and ending offsets to a doclist.
-**
-** If the doclist is setup to handle only positions, then insert
-** the position only and ignore the offsets.
-*/
-static void docListAddPosOffset(
- DocList *d, /* Doclist under construction */
- int iColumn, /* Column the inserted term is part of */
- int iPos, /* Position of the inserted term */
- int iStartOffset, /* Starting offset of inserted term */
- int iEndOffset /* Ending offset of inserted term */
-){
- assert( d->iType>=DL_POSITIONS );
- addPos(d, iColumn, iPos);
- if( d->iType==DL_POSITIONS_OFFSETS ){
- assert( iStartOffset>=d->iLastOffset );
- appendVarint(d, iStartOffset-d->iLastOffset);
- d->iLastOffset = iStartOffset;
- assert( iEndOffset>=iStartOffset );
- appendVarint(d, iEndOffset-iStartOffset);
- }
- appendVarint(d, POS_END); /* add new terminator */
-}
-
-/*
-** A DocListReader object is a cursor into a doclist. Initialize
-** the cursor to the beginning of the doclist by calling readerInit().
-** Then use routines
-**
-** peekDocid()
-** readDocid()
-** readPosition()
-** skipPositionList()
-** and so forth...
-**
-** to read information out of the doclist. When we reach the end
-** of the doclist, atEnd() returns TRUE.
-*/
-typedef struct DocListReader {
- DocList *pDoclist; /* The document list we are stepping through */
- char *p; /* Pointer to next unread byte in the doclist */
- int iLastColumn;
- int iLastPos; /* the last position read, or -1 when not in a position list */
-} DocListReader;
-
-/*
-** Initialize the DocListReader r to point to the beginning of pDoclist.
-*/
-static void readerInit(DocListReader *r, DocList *pDoclist){
- r->pDoclist = pDoclist;
- if( pDoclist!=NULL ){
- r->p = pDoclist->pData;
- }
- r->iLastColumn = -1;
- r->iLastPos = -1;
-}
-
-/*
-** Return TRUE if we have reached then end of pReader and there is
-** nothing else left to read.
-*/
-static int atEnd(DocListReader *pReader){
- return pReader->pDoclist==0 || (pReader->p >= docListEnd(pReader->pDoclist));
-}
-
-/* Peek at the next docid without advancing the read pointer.
-*/
-static sqlite_int64 peekDocid(DocListReader *pReader){
- sqlite_int64 ret;
- assert( !atEnd(pReader) );
- assert( pReader->iLastPos==-1 );
- getVarint(pReader->p, &ret);
- return ret;
-}
-
-/* Read the next docid. See also nextDocid().
-*/
-static sqlite_int64 readDocid(DocListReader *pReader){
- sqlite_int64 ret;
- assert( !atEnd(pReader) );
- assert( pReader->iLastPos==-1 );
- pReader->p += getVarint(pReader->p, &ret);
- if( pReader->pDoclist->iType>=DL_POSITIONS ){
- pReader->iLastColumn = 0;
- pReader->iLastPos = 0;
- }
- return ret;
-}
-
-/* Read the next position and column index from a position list.
- * Returns the position, or -1 at the end of the list. */
-static int readPosition(DocListReader *pReader, int *iColumn){
- int i;
- int iType = pReader->pDoclist->iType;
-
- if( pReader->iLastPos==-1 ){
- return -1;
- }
- assert( !atEnd(pReader) );
-
- if( iTypep += getVarint32(pReader->p, &i);
- if( i==POS_END ){
- pReader->iLastColumn = pReader->iLastPos = -1;
- *iColumn = -1;
- return -1;
- }
- if( i==POS_COLUMN ){
- pReader->p += getVarint32(pReader->p, &pReader->iLastColumn);
- pReader->iLastPos = 0;
- pReader->p += getVarint32(pReader->p, &i);
- assert( i>=POS_BASE );
- }
- pReader->iLastPos += ((int) i)-POS_BASE;
- if( iType>=DL_POSITIONS_OFFSETS ){
- /* Skip over offsets, ignoring them for now. */
- int iStart, iEnd;
- pReader->p += getVarint32(pReader->p, &iStart);
- pReader->p += getVarint32(pReader->p, &iEnd);
- }
- *iColumn = pReader->iLastColumn;
- return pReader->iLastPos;
-}
-
-/* Skip past the end of a position list. */
-static void skipPositionList(DocListReader *pReader){
- DocList *p = pReader->pDoclist;
- if( p && p->iType>=DL_POSITIONS ){
- int iColumn;
- while( readPosition(pReader, &iColumn)!=-1 ){}
- }
-}
-
-/* Skip over a docid, including its position list if the doclist has
- * positions. */
-static void skipDocument(DocListReader *pReader){
- readDocid(pReader);
- skipPositionList(pReader);
-}
-
-/* Skip past all docids which are less than [iDocid]. Returns 1 if a docid
- * matching [iDocid] was found. */
-static int skipToDocid(DocListReader *pReader, sqlite_int64 iDocid){
- sqlite_int64 d = 0;
- while( !atEnd(pReader) && (d=peekDocid(pReader))iType>=DL_POSITIONS ){
- int iPos, iCol;
- const char *zDiv = "";
- printf("(");
- while( (iPos = readPosition(&r, &iCol))>=0 ){
- printf("%s%d:%d", zDiv, iCol, iPos);
- zDiv = ":";
- }
- printf(")");
- }
- }
- printf("\n");
- fflush(stdout);
-}
-#endif /* SQLITE_DEBUG */
-
-/* Trim the given doclist to contain only positions in column
- * [iRestrictColumn]. */
-static void docListRestrictColumn(DocList *in, int iRestrictColumn){
- DocListReader r;
- DocList out;
-
- assert( in->iType>=DL_POSITIONS );
- readerInit(&r, in);
- docListInit(&out, DL_POSITIONS, NULL, 0);
-
- while( !atEnd(&r) ){
- sqlite_int64 iDocid = readDocid(&r);
- int iPos, iColumn;
-
- docListAddDocid(&out, iDocid);
- while( (iPos = readPosition(&r, &iColumn)) != -1 ){
- if( iColumn==iRestrictColumn ){
- docListAddPos(&out, iColumn, iPos);
- }
- }
- }
-
- docListDestroy(in);
- *in = out;
-}
-
-/* Trim the given doclist by discarding any docids without any remaining
- * positions. */
-static void docListDiscardEmpty(DocList *in) {
- DocListReader r;
- DocList out;
-
- /* TODO: It would be nice to implement this operation in place; that
- * could save a significant amount of memory in queries with long doclists. */
- assert( in->iType>=DL_POSITIONS );
- readerInit(&r, in);
- docListInit(&out, DL_POSITIONS, NULL, 0);
-
- while( !atEnd(&r) ){
- sqlite_int64 iDocid = readDocid(&r);
- int match = 0;
- int iPos, iColumn;
- while( (iPos = readPosition(&r, &iColumn)) != -1 ){
- if( !match ){
- docListAddDocid(&out, iDocid);
- match = 1;
- }
- docListAddPos(&out, iColumn, iPos);
- }
- }
-
- docListDestroy(in);
- *in = out;
-}
-
-/* Helper function for docListUpdate() and docListAccumulate().
-** Splices a doclist element into the doclist represented by r,
-** leaving r pointing after the newly spliced element.
-*/
-static void docListSpliceElement(DocListReader *r, sqlite_int64 iDocid,
- const char *pSource, int nSource){
- DocList *d = r->pDoclist;
- char *pTarget;
- int nTarget, found;
-
- found = skipToDocid(r, iDocid);
-
- /* Describe slice in d to place pSource/nSource. */
- pTarget = r->p;
- if( found ){
- skipDocument(r);
- nTarget = r->p-pTarget;
- }else{
- nTarget = 0;
- }
-
- /* The sense of the following is that there are three possibilities.
- ** If nTarget==nSource, we should not move any memory nor realloc.
- ** If nTarget>nSource, trim target and realloc.
- ** If nTargetnSource ){
- memmove(pTarget+nSource, pTarget+nTarget, docListEnd(d)-(pTarget+nTarget));
- }
- if( nTarget!=nSource ){
- int iDoclist = pTarget-d->pData;
- d->pData = realloc(d->pData, d->nData+nSource-nTarget);
- pTarget = d->pData+iDoclist;
- }
- if( nTargetnData += nSource-nTarget;
- r->p = pTarget+nSource;
-}
-
-/* Insert/update pUpdate into the doclist. */
-static void docListUpdate(DocList *d, DocList *pUpdate){
- DocListReader reader;
-
- assert( d!=NULL && pUpdate!=NULL );
- assert( d->iType==pUpdate->iType);
-
- readerInit(&reader, d);
- docListSpliceElement(&reader, firstDocid(pUpdate),
- pUpdate->pData, pUpdate->nData);
-}
-
-/* Propagate elements from pUpdate to pAcc, overwriting elements with
-** matching docids.
-*/
-static void docListAccumulate(DocList *pAcc, DocList *pUpdate){
- DocListReader accReader, updateReader;
-
- /* Handle edge cases where one doclist is empty. */
- assert( pAcc!=NULL );
- if( pUpdate==NULL || pUpdate->nData==0 ) return;
- if( pAcc->nData==0 ){
- pAcc->pData = malloc(pUpdate->nData);
- memcpy(pAcc->pData, pUpdate->pData, pUpdate->nData);
- pAcc->nData = pUpdate->nData;
- return;
- }
-
- readerInit(&accReader, pAcc);
- readerInit(&updateReader, pUpdate);
-
- while( !atEnd(&updateReader) ){
- char *pSource = updateReader.p;
- sqlite_int64 iDocid = readDocid(&updateReader);
- skipPositionList(&updateReader);
- docListSpliceElement(&accReader, iDocid, pSource, updateReader.p-pSource);
- }
-}
-
-/*
-** Read the next docid off of pIn. Return 0 if we reach the end.
-*
-* TODO: This assumes that docids are never 0, but they may actually be 0 since
-* users can choose docids when inserting into a full-text table. Fix this.
-*/
-static sqlite_int64 nextDocid(DocListReader *pIn){
- skipPositionList(pIn);
- return atEnd(pIn) ? 0 : readDocid(pIn);
-}
-
-/*
-** pLeft and pRight are two DocListReaders that are pointing to
-** positions lists of the same document: iDocid.
-**
-** If there are no instances in pLeft or pRight where the position
-** of pLeft is one less than the position of pRight, then this
-** routine adds nothing to pOut.
-**
-** If there are one or more instances where positions from pLeft
-** are exactly one less than positions from pRight, then add a new
-** document record to pOut. If pOut wants to hold positions, then
-** include the positions from pRight that are one more than a
-** position in pLeft. In other words: pRight.iPos==pLeft.iPos+1.
-**
-** pLeft and pRight are left pointing at the next document record.
-*/
-static void mergePosList(
- DocListReader *pLeft, /* Left position list */
- DocListReader *pRight, /* Right position list */
- sqlite_int64 iDocid, /* The docid from pLeft and pRight */
- DocList *pOut /* Write the merged document record here */
-){
- int iLeftCol, iLeftPos = readPosition(pLeft, &iLeftCol);
- int iRightCol, iRightPos = readPosition(pRight, &iRightCol);
- int match = 0;
-
- /* Loop until we've reached the end of both position lists. */
- while( iLeftPos!=-1 && iRightPos!=-1 ){
- if( iLeftCol==iRightCol && iLeftPos+1==iRightPos ){
- if( !match ){
- docListAddDocid(pOut, iDocid);
- match = 1;
- }
- if( pOut->iType>=DL_POSITIONS ){
- docListAddPos(pOut, iRightCol, iRightPos);
- }
- iLeftPos = readPosition(pLeft, &iLeftCol);
- iRightPos = readPosition(pRight, &iRightCol);
- }else if( iRightCol=0 ) skipPositionList(pLeft);
- if( iRightPos>=0 ) skipPositionList(pRight);
-}
-
-/* We have two doclists: pLeft and pRight.
-** Write the phrase intersection of these two doclists into pOut.
-**
-** A phrase intersection means that two documents only match
-** if pLeft.iPos+1==pRight.iPos.
-**
-** The output pOut may or may not contain positions. If pOut
-** does contain positions, they are the positions of pRight.
-*/
-static void docListPhraseMerge(
- DocList *pLeft, /* Doclist resulting from the words on the left */
- DocList *pRight, /* Doclist for the next word to the right */
- DocList *pOut /* Write the combined doclist here */
-){
- DocListReader left, right;
- sqlite_int64 docidLeft, docidRight;
-
- readerInit(&left, pLeft);
- readerInit(&right, pRight);
- docidLeft = nextDocid(&left);
- docidRight = nextDocid(&right);
-
- while( docidLeft>0 && docidRight>0 ){
- if( docidLeftiType0 && docidRight>0 ){
- if( docidLeft0 && docidRight>0 ){
- if( docidLeft<=docidRight ){
- docListAddDocid(pOut, docidLeft);
- }else{
- docListAddDocid(pOut, docidRight);
- }
- priorLeft = docidLeft;
- if( docidLeft<=docidRight ){
- docidLeft = nextDocid(&left);
- }
- if( docidRight>0 && docidRight<=priorLeft ){
- docidRight = nextDocid(&right);
- }
- }
- while( docidLeft>0 ){
- docListAddDocid(pOut, docidLeft);
- docidLeft = nextDocid(&left);
- }
- while( docidRight>0 ){
- docListAddDocid(pOut, docidRight);
- docidRight = nextDocid(&right);
- }
-}
-
-/* We have two doclists: pLeft and pRight.
-** Write into pOut all documents that occur in pLeft but not
-** in pRight.
-**
-** Only docids are matched. Position information is ignored.
-**
-** The output pOut never holds positions.
-*/
-static void docListExceptMerge(
- DocList *pLeft, /* Doclist resulting from the words on the left */
- DocList *pRight, /* Doclist for the next word to the right */
- DocList *pOut /* Write the combined doclist here */
-){
- DocListReader left, right;
- sqlite_int64 docidLeft, docidRight, priorLeft;
-
- readerInit(&left, pLeft);
- readerInit(&right, pRight);
- docidLeft = nextDocid(&left);
- docidRight = nextDocid(&right);
-
- while( docidLeft>0 && docidRight>0 ){
- priorLeft = docidLeft;
- if( docidLeft0 && docidRight<=priorLeft ){
- docidRight = nextDocid(&right);
- }
- }
- while( docidLeft>0 ){
- docListAddDocid(pOut, docidLeft);
- docidLeft = nextDocid(&left);
- }
-}
-
-static char *string_dup_n(const char *s, int n){
- char *str = malloc(n + 1);
- memcpy(str, s, n);
- str[n] = '\0';
- return str;
-}
-
-/* Duplicate a string; the caller must free() the returned string.
- * (We don't use strdup() since it is not part of the standard C library and
- * may not be available everywhere.) */
-static char *string_dup(const char *s){
- return string_dup_n(s, strlen(s));
-}
-
-/* Format a string, replacing each occurrence of the % character with
- * zDb.zName. This may be more convenient than sqlite_mprintf()
- * when one string is used repeatedly in a format string.
- * The caller must free() the returned string. */
-static char *string_format(const char *zFormat,
- const char *zDb, const char *zName){
- const char *p;
- size_t len = 0;
- size_t nDb = strlen(zDb);
- size_t nName = strlen(zName);
- size_t nFullTableName = nDb+1+nName;
- char *result;
- char *r;
-
- /* first compute length needed */
- for(p = zFormat ; *p ; ++p){
- len += (*p=='%' ? nFullTableName : 1);
- }
- len += 1; /* for null terminator */
-
- r = result = malloc(len);
- for(p = zFormat; *p; ++p){
- if( *p=='%' ){
- memcpy(r, zDb, nDb);
- r += nDb;
- *r++ = '.';
- memcpy(r, zName, nName);
- r += nName;
- } else {
- *r++ = *p;
- }
- }
- *r++ = '\0';
- assert( r == result + len );
- return result;
-}
-
-static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
- const char *zFormat){
- char *zCommand = string_format(zFormat, zDb, zName);
- int rc;
- TRACE(("FTS1 sql: %s\n", zCommand));
- rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
- free(zCommand);
- return rc;
-}
-
-static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
- sqlite3_stmt **ppStmt, const char *zFormat){
- char *zCommand = string_format(zFormat, zDb, zName);
- int rc;
- TRACE(("FTS1 prepare: %s\n", zCommand));
- rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
- free(zCommand);
- return rc;
-}
-
-/* end utility functions */
-
-/* Forward reference */
-typedef struct fulltext_vtab fulltext_vtab;
-
-/* A single term in a query is represented by an instances of
-** the following structure.
-*/
-typedef struct QueryTerm {
- short int nPhrase; /* How many following terms are part of the same phrase */
- short int iPhrase; /* This is the i-th term of a phrase. */
- short int iColumn; /* Column of the index that must match this term */
- signed char isOr; /* this term is preceded by "OR" */
- signed char isNot; /* this term is preceded by "-" */
- char *pTerm; /* text of the term. '\000' terminated. malloced */
- int nTerm; /* Number of bytes in pTerm[] */
-} QueryTerm;
-
-
-/* A query string is parsed into a Query structure.
- *
- * We could, in theory, allow query strings to be complicated
- * nested expressions with precedence determined by parentheses.
- * But none of the major search engines do this. (Perhaps the
- * feeling is that an parenthesized expression is two complex of
- * an idea for the average user to grasp.) Taking our lead from
- * the major search engines, we will allow queries to be a list
- * of terms (with an implied AND operator) or phrases in double-quotes,
- * with a single optional "-" before each non-phrase term to designate
- * negation and an optional OR connector.
- *
- * OR binds more tightly than the implied AND, which is what the
- * major search engines seem to do. So, for example:
- *
- * [one two OR three] ==> one AND (two OR three)
- * [one OR two three] ==> (one OR two) AND three
- *
- * A "-" before a term matches all entries that lack that term.
- * The "-" must occur immediately before the term with in intervening
- * space. This is how the search engines do it.
- *
- * A NOT term cannot be the right-hand operand of an OR. If this
- * occurs in the query string, the NOT is ignored:
- *
- * [one OR -two] ==> one OR two
- *
- */
-typedef struct Query {
- fulltext_vtab *pFts; /* The full text index */
- int nTerms; /* Number of terms in the query */
- QueryTerm *pTerms; /* Array of terms. Space obtained from malloc() */
- int nextIsOr; /* Set the isOr flag on the next inserted term */
- int nextColumn; /* Next word parsed must be in this column */
- int dfltColumn; /* The default column */
-} Query;
-
-
-/*
-** An instance of the following structure keeps track of generated
-** matching-word offset information and snippets.
-*/
-typedef struct Snippet {
- int nMatch; /* Total number of matches */
- int nAlloc; /* Space allocated for aMatch[] */
- struct snippetMatch { /* One entry for each matching term */
- char snStatus; /* Status flag for use while constructing snippets */
- short int iCol; /* The column that contains the match */
- short int iTerm; /* The index in Query.pTerms[] of the matching term */
- short int nByte; /* Number of bytes in the term */
- int iStart; /* The offset to the first character of the term */
- } *aMatch; /* Points to space obtained from malloc */
- char *zOffset; /* Text rendering of aMatch[] */
- int nOffset; /* strlen(zOffset) */
- char *zSnippet; /* Snippet text */
- int nSnippet; /* strlen(zSnippet) */
-} Snippet;
-
-
-typedef enum QueryType {
- QUERY_GENERIC, /* table scan */
- QUERY_ROWID, /* lookup by rowid */
- QUERY_FULLTEXT /* QUERY_FULLTEXT + [i] is a full-text search for column i*/
-} QueryType;
-
-/* TODO(shess) CHUNK_MAX controls how much data we allow in segment 0
-** before we start aggregating into larger segments. Lower CHUNK_MAX
-** means that for a given input we have more individual segments per
-** term, which means more rows in the table and a bigger index (due to
-** both more rows and bigger rowids). But it also reduces the average
-** cost of adding new elements to the segment 0 doclist, and it seems
-** to reduce the number of pages read and written during inserts. 256
-** was chosen by measuring insertion times for a certain input (first
-** 10k documents of Enron corpus), though including query performance
-** in the decision may argue for a larger value.
-*/
-#define CHUNK_MAX 256
-
-typedef enum fulltext_statement {
- CONTENT_INSERT_STMT,
- CONTENT_SELECT_STMT,
- CONTENT_UPDATE_STMT,
- CONTENT_DELETE_STMT,
-
- TERM_SELECT_STMT,
- TERM_SELECT_ALL_STMT,
- TERM_INSERT_STMT,
- TERM_UPDATE_STMT,
- TERM_DELETE_STMT,
-
- MAX_STMT /* Always at end! */
-} fulltext_statement;
-
-/* These must exactly match the enum above. */
-/* TODO(adam): Is there some risk that a statement (in particular,
-** pTermSelectStmt) will be used in two cursors at once, e.g. if a
-** query joins a virtual table to itself? If so perhaps we should
-** move some of these to the cursor object.
-*/
-static const char *const fulltext_zStatement[MAX_STMT] = {
- /* CONTENT_INSERT */ NULL, /* generated in contentInsertStatement() */
- /* CONTENT_SELECT */ "select * from %_content where rowid = ?",
- /* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */
- /* CONTENT_DELETE */ "delete from %_content where rowid = ?",
-
- /* TERM_SELECT */
- "select rowid, doclist from %_term where term = ? and segment = ?",
- /* TERM_SELECT_ALL */
- "select doclist from %_term where term = ? order by segment",
- /* TERM_INSERT */
- "insert into %_term (rowid, term, segment, doclist) values (?, ?, ?, ?)",
- /* TERM_UPDATE */ "update %_term set doclist = ? where rowid = ?",
- /* TERM_DELETE */ "delete from %_term where rowid = ?",
-};
-
-/*
-** A connection to a fulltext index is an instance of the following
-** structure. The xCreate and xConnect methods create an instance
-** of this structure and xDestroy and xDisconnect free that instance.
-** All other methods receive a pointer to the structure as one of their
-** arguments.
-*/
-struct fulltext_vtab {
- sqlite3_vtab base; /* Base class used by SQLite core */
- sqlite3 *db; /* The database connection */
- const char *zDb; /* logical database name */
- const char *zName; /* virtual table name */
- int nColumn; /* number of columns in virtual table */
- char **azColumn; /* column names. malloced */
- char **azContentColumn; /* column names in content table; malloced */
- sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
-
- /* Precompiled statements which we keep as long as the table is
- ** open.
- */
- sqlite3_stmt *pFulltextStatements[MAX_STMT];
-};
-
-/*
-** When the core wants to do a query, it create a cursor using a
-** call to xOpen. This structure is an instance of a cursor. It
-** is destroyed by xClose.
-*/
-typedef struct fulltext_cursor {
- sqlite3_vtab_cursor base; /* Base class used by SQLite core */
- QueryType iCursorType; /* Copy of sqlite3_index_info.idxNum */
- sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
- int eof; /* True if at End Of Results */
- Query q; /* Parsed query string */
- Snippet snippet; /* Cached snippet for the current row */
- int iColumn; /* Column being searched */
- DocListReader result; /* used when iCursorType == QUERY_FULLTEXT */
-} fulltext_cursor;
-
-static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
- return (fulltext_vtab *) c->base.pVtab;
-}
-
-static const sqlite3_module fulltextModule; /* forward declaration */
-
-/* Append a list of strings separated by commas to a StringBuffer. */
-static void appendList(StringBuffer *sb, int nString, char **azString){
- int i;
- for(i=0; i0 ) append(sb, ", ");
- append(sb, azString[i]);
- }
-}
-
-/* Return a dynamically generated statement of the form
- * insert into %_content (rowid, ...) values (?, ...)
- */
-static const char *contentInsertStatement(fulltext_vtab *v){
- StringBuffer sb;
- int i;
-
- initStringBuffer(&sb);
- append(&sb, "insert into %_content (rowid, ");
- appendList(&sb, v->nColumn, v->azContentColumn);
- append(&sb, ") values (?");
- for(i=0; inColumn; ++i)
- append(&sb, ", ?");
- append(&sb, ")");
- return sb.s;
-}
-
-/* Return a dynamically generated statement of the form
- * update %_content set [col_0] = ?, [col_1] = ?, ...
- * where rowid = ?
- */
-static const char *contentUpdateStatement(fulltext_vtab *v){
- StringBuffer sb;
- int i;
-
- initStringBuffer(&sb);
- append(&sb, "update %_content set ");
- for(i=0; inColumn; ++i) {
- if( i>0 ){
- append(&sb, ", ");
- }
- append(&sb, v->azContentColumn[i]);
- append(&sb, " = ?");
- }
- append(&sb, " where rowid = ?");
- return sb.s;
-}
-
-/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
-** If the indicated statement has never been prepared, it is prepared
-** and cached, otherwise the cached version is reset.
-*/
-static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- assert( iStmtpFulltextStatements[iStmt]==NULL ){
- const char *zStmt;
- int rc;
- switch( iStmt ){
- case CONTENT_INSERT_STMT:
- zStmt = contentInsertStatement(v); break;
- case CONTENT_UPDATE_STMT:
- zStmt = contentUpdateStatement(v); break;
- default:
- zStmt = fulltext_zStatement[iStmt];
- }
- rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
- zStmt);
- if( zStmt != fulltext_zStatement[iStmt]) free((void *) zStmt);
- if( rc!=SQLITE_OK ) return rc;
- } else {
- int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- *ppStmt = v->pFulltextStatements[iStmt];
- return SQLITE_OK;
-}
-
-/* Step the indicated statement, handling errors SQLITE_BUSY (by
-** retrying) and SQLITE_SCHEMA (by re-preparing and transferring
-** bindings to the new statement).
-** TODO(adam): We should extend this function so that it can work with
-** statements declared locally, not only globally cached statements.
-*/
-static int sql_step_statement(fulltext_vtab *v, fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- int rc;
- sqlite3_stmt *s = *ppStmt;
- assert( iStmtpFulltextStatements[iStmt] );
-
- while( (rc=sqlite3_step(s))!=SQLITE_DONE && rc!=SQLITE_ROW ){
- if( rc==SQLITE_BUSY ) continue;
- if( rc!=SQLITE_ERROR ) return rc;
-
- /* If an SQLITE_SCHEMA error has occurred, then finalizing this
- * statement is going to delete the fulltext_vtab structure. If
- * the statement just executed is in the pFulltextStatements[]
- * array, it will be finalized twice. So remove it before
- * calling sqlite3_finalize().
- */
- v->pFulltextStatements[iStmt] = NULL;
- rc = sqlite3_finalize(s);
- break;
- }
- return rc;
-
- err:
- sqlite3_finalize(s);
- return rc;
-}
-
-/* Like sql_step_statement(), but convert SQLITE_DONE to SQLITE_OK.
-** Useful for statements like UPDATE, where we expect no results.
-*/
-static int sql_single_step_statement(fulltext_vtab *v,
- fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- int rc = sql_step_statement(v, iStmt, ppStmt);
- return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
-}
-
-/* insert into %_content (rowid, ...) values ([rowid], [pValues]) */
-static int content_insert(fulltext_vtab *v, sqlite3_value *rowid,
- sqlite3_value **pValues){
- sqlite3_stmt *s;
- int i;
- int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_value(s, 1, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- for(i=0; inColumn; ++i){
- rc = sqlite3_bind_value(s, 2+i, pValues[i]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- return sql_single_step_statement(v, CONTENT_INSERT_STMT, &s);
-}
-
-/* update %_content set col0 = pValues[0], col1 = pValues[1], ...
- * where rowid = [iRowid] */
-static int content_update(fulltext_vtab *v, sqlite3_value **pValues,
- sqlite_int64 iRowid){
- sqlite3_stmt *s;
- int i;
- int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- for(i=0; inColumn; ++i){
- rc = sqlite3_bind_value(s, 1+i, pValues[i]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- rc = sqlite3_bind_int64(s, 1+v->nColumn, iRowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, CONTENT_UPDATE_STMT, &s);
-}
-
-static void freeStringArray(int nString, const char **pString){
- int i;
-
- for (i=0 ; i < nString ; ++i) {
- if( pString[i]!=NULL ) free((void *) pString[i]);
- }
- free((void *) pString);
-}
-
-/* select * from %_content where rowid = [iRow]
- * The caller must delete the returned array and all strings in it.
- * null fields will be NULL in the returned array.
- *
- * TODO: Perhaps we should return pointer/length strings here for consistency
- * with other code which uses pointer/length. */
-static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
- const char ***pValues){
- sqlite3_stmt *s;
- const char **values;
- int i;
- int rc;
-
- *pValues = NULL;
-
- rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
- if( rc!=SQLITE_ROW ) return rc;
-
- values = (const char **) malloc(v->nColumn * sizeof(const char *));
- for(i=0; inColumn; ++i){
- if( sqlite3_column_type(s, i)==SQLITE_NULL ){
- values[i] = NULL;
- }else{
- values[i] = string_dup((char*)sqlite3_column_text(s, i));
- }
- }
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ){
- *pValues = values;
- return SQLITE_OK;
- }
-
- freeStringArray(v->nColumn, values);
- return rc;
-}
-
-/* delete from %_content where rowid = [iRow ] */
-static int content_delete(fulltext_vtab *v, sqlite_int64 iRow){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, CONTENT_DELETE_STMT, &s);
-}
-
-/* select rowid, doclist from %_term
- * where term = [pTerm] and segment = [iSegment]
- * If found, returns SQLITE_ROW; the caller must free the
- * returned doclist. If no rows found, returns SQLITE_DONE. */
-static int term_select(fulltext_vtab *v, const char *pTerm, int nTerm,
- int iSegment,
- sqlite_int64 *rowid, DocList *out){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(s, 1, pTerm, nTerm, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 2, iSegment);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_step_statement(v, TERM_SELECT_STMT, &s);
- if( rc!=SQLITE_ROW ) return rc;
-
- *rowid = sqlite3_column_int64(s, 0);
- docListInit(out, DL_DEFAULT,
- sqlite3_column_blob(s, 1), sqlite3_column_bytes(s, 1));
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- return rc==SQLITE_DONE ? SQLITE_ROW : rc;
-}
-
-/* Load the segment doclists for term pTerm and merge them in
-** appropriate order into out. Returns SQLITE_OK if successful. If
-** there are no segments for pTerm, successfully returns an empty
-** doclist in out.
-**
-** Each document consists of 1 or more "columns". The number of
-** columns is v->nColumn. If iColumn==v->nColumn, then return
-** position information about all columns. If iColumnnColumn,
-** then only return position information about the iColumn-th column
-** (where the first column is 0).
-*/
-static int term_select_all(
- fulltext_vtab *v, /* The fulltext index we are querying against */
- int iColumn, /* If nColumn ){ /* querying a single column */
- docListRestrictColumn(&old, iColumn);
- }
-
- /* doclist contains the newer data, so write it over old. Then
- ** steal accumulated result for doclist.
- */
- docListAccumulate(&old, &doclist);
- docListDestroy(&doclist);
- doclist = old;
- }
- if( rc!=SQLITE_DONE ){
- docListDestroy(&doclist);
- return rc;
- }
-
- docListDiscardEmpty(&doclist);
- *out = doclist;
- return SQLITE_OK;
-}
-
-/* insert into %_term (rowid, term, segment, doclist)
- values ([piRowid], [pTerm], [iSegment], [doclist])
-** Lets sqlite select rowid if piRowid is NULL, else uses *piRowid.
-**
-** NOTE(shess) piRowid is IN, with values of "space of int64" plus
-** null, it is not used to pass data back to the caller.
-*/
-static int term_insert(fulltext_vtab *v, sqlite_int64 *piRowid,
- const char *pTerm, int nTerm,
- int iSegment, DocList *doclist){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_INSERT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- if( piRowid==NULL ){
- rc = sqlite3_bind_null(s, 1);
- }else{
- rc = sqlite3_bind_int64(s, 1, *piRowid);
- }
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(s, 2, pTerm, nTerm, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 3, iSegment);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_blob(s, 4, doclist->pData, doclist->nData, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, TERM_INSERT_STMT, &s);
-}
-
-/* update %_term set doclist = [doclist] where rowid = [rowid] */
-static int term_update(fulltext_vtab *v, sqlite_int64 rowid,
- DocList *doclist){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_UPDATE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_blob(s, 1, doclist->pData, doclist->nData, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, TERM_UPDATE_STMT, &s);
-}
-
-static int term_delete(fulltext_vtab *v, sqlite_int64 rowid){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, TERM_DELETE_STMT, &s);
-}
-
-/*
-** Free the memory used to contain a fulltext_vtab structure.
-*/
-static void fulltext_vtab_destroy(fulltext_vtab *v){
- int iStmt, i;
-
- TRACE(("FTS1 Destroy %p\n", v));
- for( iStmt=0; iStmtpFulltextStatements[iStmt]!=NULL ){
- sqlite3_finalize(v->pFulltextStatements[iStmt]);
- v->pFulltextStatements[iStmt] = NULL;
- }
- }
-
- if( v->pTokenizer!=NULL ){
- v->pTokenizer->pModule->xDestroy(v->pTokenizer);
- v->pTokenizer = NULL;
- }
-
- free(v->azColumn);
- for(i = 0; i < v->nColumn; ++i) {
- sqlite3_free(v->azContentColumn[i]);
- }
- free(v->azContentColumn);
- free(v);
-}
-
-/*
-** Token types for parsing the arguments to xConnect or xCreate.
-*/
-#define TOKEN_EOF 0 /* End of file */
-#define TOKEN_SPACE 1 /* Any kind of whitespace */
-#define TOKEN_ID 2 /* An identifier */
-#define TOKEN_STRING 3 /* A string literal */
-#define TOKEN_PUNCT 4 /* A single punctuation character */
-
-/*
-** If X is a character that can be used in an identifier then
-** IdChar(X) will be true. Otherwise it is false.
-**
-** For ASCII, any character with the high-order bit set is
-** allowed in an identifier. For 7-bit characters,
-** sqlite3IsIdChar[X] must be 1.
-**
-** Ticket #1066. the SQL standard does not allow '$' in the
-** middle of identfiers. But many SQL implementations do.
-** SQLite will allow '$' in identifiers for compatibility.
-** But the feature is undocumented.
-*/
-static const char isIdChar[] = {
-/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
- 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
-};
-#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && isIdChar[c-0x20]))
-
-
-/*
-** Return the length of the token that begins at z[0].
-** Store the token type in *tokenType before returning.
-*/
-static int getToken(const char *z, int *tokenType){
- int i, c;
- switch( *z ){
- case 0: {
- *tokenType = TOKEN_EOF;
- return 0;
- }
- case ' ': case '\t': case '\n': case '\f': case '\r': {
- for(i=1; safe_isspace(z[i]); i++){}
- *tokenType = TOKEN_SPACE;
- return i;
- }
- case '`':
- case '\'':
- case '"': {
- int delim = z[0];
- for(i=1; (c=z[i])!=0; i++){
- if( c==delim ){
- if( z[i+1]==delim ){
- i++;
- }else{
- break;
- }
- }
- }
- *tokenType = TOKEN_STRING;
- return i + (c!=0);
- }
- case '[': {
- for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
- *tokenType = TOKEN_ID;
- return i;
- }
- default: {
- if( !IdChar(*z) ){
- break;
- }
- for(i=1; IdChar(z[i]); i++){}
- *tokenType = TOKEN_ID;
- return i;
- }
- }
- *tokenType = TOKEN_PUNCT;
- return 1;
-}
-
-/*
-** A token extracted from a string is an instance of the following
-** structure.
-*/
-typedef struct Token {
- const char *z; /* Pointer to token text. Not '\000' terminated */
- short int n; /* Length of the token text in bytes. */
-} Token;
-
-/*
-** Given a input string (which is really one of the argv[] parameters
-** passed into xConnect or xCreate) split the string up into tokens.
-** Return an array of pointers to '\000' terminated strings, one string
-** for each non-whitespace token.
-**
-** The returned array is terminated by a single NULL pointer.
-**
-** Space to hold the returned array is obtained from a single
-** malloc and should be freed by passing the return value to free().
-** The individual strings within the token list are all a part of
-** the single memory allocation and will all be freed at once.
-*/
-static char **tokenizeString(const char *z, int *pnToken){
- int nToken = 0;
- Token *aToken = malloc( strlen(z) * sizeof(aToken[0]) );
- int n = 1;
- int e, i;
- int totalSize = 0;
- char **azToken;
- char *zCopy;
- while( n>0 ){
- n = getToken(z, &e);
- if( e!=TOKEN_SPACE ){
- aToken[nToken].z = z;
- aToken[nToken].n = n;
- nToken++;
- totalSize += n+1;
- }
- z += n;
- }
- azToken = (char**)malloc( nToken*sizeof(char*) + totalSize );
- zCopy = (char*)&azToken[nToken];
- nToken--;
- for(i=0; i=0 ){
- azIn[j] = azIn[i];
- }
- j++;
- }
- }
- azIn[j] = 0;
- }
-}
-
-
-/*
-** Find the first alphanumeric token in the string zIn. Null-terminate
-** this token. Remove any quotation marks. And return a pointer to
-** the result.
-*/
-static char *firstToken(char *zIn, char **pzTail){
- int n, ttype;
- while(1){
- n = getToken(zIn, &ttype);
- if( ttype==TOKEN_SPACE ){
- zIn += n;
- }else if( ttype==TOKEN_EOF ){
- *pzTail = zIn;
- return 0;
- }else{
- zIn[n] = 0;
- *pzTail = &zIn[1];
- dequoteString(zIn);
- return zIn;
- }
- }
- /*NOTREACHED*/
-}
-
-/* Return true if...
-**
-** * s begins with the string t, ignoring case
-** * s is longer than t
-** * The first character of s beyond t is not a alphanumeric
-**
-** Ignore leading space in *s.
-**
-** To put it another way, return true if the first token of
-** s[] is t[].
-*/
-static int startsWith(const char *s, const char *t){
- while( safe_isspace(*s) ){ s++; }
- while( *t ){
- if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0;
- }
- return *s!='_' && !safe_isalnum(*s);
-}
-
-/*
-** An instance of this structure defines the "spec" of a
-** full text index. This structure is populated by parseSpec
-** and use by fulltextConnect and fulltextCreate.
-*/
-typedef struct TableSpec {
- const char *zDb; /* Logical database name */
- const char *zName; /* Name of the full-text index */
- int nColumn; /* Number of columns to be indexed */
- char **azColumn; /* Original names of columns to be indexed */
- char **azContentColumn; /* Column names for %_content */
- char **azTokenizer; /* Name of tokenizer and its arguments */
-} TableSpec;
-
-/*
-** Reclaim all of the memory used by a TableSpec
-*/
-static void clearTableSpec(TableSpec *p) {
- free(p->azColumn);
- free(p->azContentColumn);
- free(p->azTokenizer);
-}
-
-/* Parse a CREATE VIRTUAL TABLE statement, which looks like this:
- *
- * CREATE VIRTUAL TABLE email
- * USING fts1(subject, body, tokenize mytokenizer(myarg))
- *
- * We return parsed information in a TableSpec structure.
- *
- */
-static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv,
- char**pzErr){
- int i, n;
- char *z, *zDummy;
- char **azArg;
- const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */
-
- assert( argc>=3 );
- /* Current interface:
- ** argv[0] - module name
- ** argv[1] - database name
- ** argv[2] - table name
- ** argv[3..] - columns, optionally followed by tokenizer specification
- ** and snippet delimiters specification.
- */
-
- /* Make a copy of the complete argv[][] array in a single allocation.
- ** The argv[][] array is read-only and transient. We can write to the
- ** copy in order to modify things and the copy is persistent.
- */
- memset(pSpec, 0, sizeof(*pSpec));
- for(i=n=0; izDb = azArg[1];
- pSpec->zName = azArg[2];
- pSpec->nColumn = 0;
- pSpec->azColumn = azArg;
- zTokenizer = "tokenize simple";
- for(i=3; inColumn] = firstToken(azArg[i], &zDummy);
- pSpec->nColumn++;
- }
- }
- if( pSpec->nColumn==0 ){
- azArg[0] = "content";
- pSpec->nColumn = 1;
- }
-
- /*
- ** Construct the list of content column names.
- **
- ** Each content column name will be of the form cNNAAAA
- ** where NN is the column number and AAAA is the sanitized
- ** column name. "sanitized" means that special characters are
- ** converted to "_". The cNN prefix guarantees that all column
- ** names are unique.
- **
- ** The AAAA suffix is not strictly necessary. It is included
- ** for the convenience of people who might examine the generated
- ** %_content table and wonder what the columns are used for.
- */
- pSpec->azContentColumn = malloc( pSpec->nColumn * sizeof(char *) );
- if( pSpec->azContentColumn==0 ){
- clearTableSpec(pSpec);
- return SQLITE_NOMEM;
- }
- for(i=0; inColumn; i++){
- char *p;
- pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]);
- for (p = pSpec->azContentColumn[i]; *p ; ++p) {
- if( !safe_isalnum(*p) ) *p = '_';
- }
- }
-
- /*
- ** Parse the tokenizer specification string.
- */
- pSpec->azTokenizer = tokenizeString(zTokenizer, &n);
- tokenListToIdList(pSpec->azTokenizer);
-
- return SQLITE_OK;
-}
-
-/*
-** Generate a CREATE TABLE statement that describes the schema of
-** the virtual table. Return a pointer to this schema string.
-**
-** Space is obtained from sqlite3_mprintf() and should be freed
-** using sqlite3_free().
-*/
-static char *fulltextSchema(
- int nColumn, /* Number of columns */
- const char *const* azColumn, /* List of columns */
- const char *zTableName /* Name of the table */
-){
- int i;
- char *zSchema, *zNext;
- const char *zSep = "(";
- zSchema = sqlite3_mprintf("CREATE TABLE x");
- for(i=0; ibase */
- v->db = db;
- v->zDb = spec->zDb; /* Freed when azColumn is freed */
- v->zName = spec->zName; /* Freed when azColumn is freed */
- v->nColumn = spec->nColumn;
- v->azContentColumn = spec->azContentColumn;
- spec->azContentColumn = 0;
- v->azColumn = spec->azColumn;
- spec->azColumn = 0;
-
- if( spec->azTokenizer==0 ){
- return SQLITE_NOMEM;
- }
- /* TODO(shess) For now, add new tokenizers as else if clauses. */
- if( spec->azTokenizer[0]==0 || startsWith(spec->azTokenizer[0], "simple") ){
- sqlite3Fts1SimpleTokenizerModule(&m);
- }else if( startsWith(spec->azTokenizer[0], "porter") ){
- sqlite3Fts1PorterTokenizerModule(&m);
- }else{
- *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]);
- rc = SQLITE_ERROR;
- goto err;
- }
- for(n=0; spec->azTokenizer[n]; n++){}
- if( n ){
- rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1],
- &v->pTokenizer);
- }else{
- rc = m->xCreate(0, 0, &v->pTokenizer);
- }
- if( rc!=SQLITE_OK ) goto err;
- v->pTokenizer->pModule = m;
-
- /* TODO: verify the existence of backing tables foo_content, foo_term */
-
- schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn,
- spec->zName);
- rc = sqlite3_declare_vtab(db, schema);
- sqlite3_free(schema);
- if( rc!=SQLITE_OK ) goto err;
-
- memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
-
- *ppVTab = &v->base;
- TRACE(("FTS1 Connect %p\n", v));
-
- return rc;
-
-err:
- fulltext_vtab_destroy(v);
- return rc;
-}
-
-static int fulltextConnect(
- sqlite3 *db,
- void *pAux,
- int argc, const char *const*argv,
- sqlite3_vtab **ppVTab,
- char **pzErr
-){
- TableSpec spec;
- int rc = parseSpec(&spec, argc, argv, pzErr);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = constructVtab(db, &spec, ppVTab, pzErr);
- clearTableSpec(&spec);
- return rc;
-}
-
- /* The %_content table holds the text of each document, with
- ** the rowid used as the docid.
- **
- ** The %_term table maps each term to a document list blob
- ** containing elements sorted by ascending docid, each element
- ** encoded as:
- **
- ** docid varint-encoded
- ** token elements:
- ** position+1 varint-encoded as delta from previous position
- ** start offset varint-encoded as delta from previous start offset
- ** end offset varint-encoded as delta from start offset
- **
- ** The sentinel position of 0 indicates the end of the token list.
- **
- ** Additionally, doclist blobs are chunked into multiple segments,
- ** using segment to order the segments. New elements are added to
- ** the segment at segment 0, until it exceeds CHUNK_MAX. Then
- ** segment 0 is deleted, and the doclist is inserted at segment 1.
- ** If there is already a doclist at segment 1, the segment 0 doclist
- ** is merged with it, the segment 1 doclist is deleted, and the
- ** merged doclist is inserted at segment 2, repeating those
- ** operations until an insert succeeds.
- **
- ** Since this structure doesn't allow us to update elements in place
- ** in case of deletion or update, these are simply written to
- ** segment 0 (with an empty token list in case of deletion), with
- ** docListAccumulate() taking care to retain lower-segment
- ** information in preference to higher-segment information.
- */
- /* TODO(shess) Provide a VACUUM type operation which both removes
- ** deleted elements which are no longer necessary, and duplicated
- ** elements. I suspect this will probably not be necessary in
- ** practice, though.
- */
-static int fulltextCreate(sqlite3 *db, void *pAux,
- int argc, const char * const *argv,
- sqlite3_vtab **ppVTab, char **pzErr){
- int rc;
- TableSpec spec;
- StringBuffer schema;
- TRACE(("FTS1 Create\n"));
-
- rc = parseSpec(&spec, argc, argv, pzErr);
- if( rc!=SQLITE_OK ) return rc;
-
- initStringBuffer(&schema);
- append(&schema, "CREATE TABLE %_content(");
- appendList(&schema, spec.nColumn, spec.azContentColumn);
- append(&schema, ")");
- rc = sql_exec(db, spec.zDb, spec.zName, schema.s);
- free(schema.s);
- if( rc!=SQLITE_OK ) goto out;
-
- rc = sql_exec(db, spec.zDb, spec.zName,
- "create table %_term(term text, segment integer, doclist blob, "
- "primary key(term, segment));");
- if( rc!=SQLITE_OK ) goto out;
-
- rc = constructVtab(db, &spec, ppVTab, pzErr);
-
-out:
- clearTableSpec(&spec);
- return rc;
-}
-
-/* Decide how to handle an SQL query. */
-static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
- int i;
- TRACE(("FTS1 BestIndex\n"));
-
- for(i=0; inConstraint; ++i){
- const struct sqlite3_index_constraint *pConstraint;
- pConstraint = &pInfo->aConstraint[i];
- if( pConstraint->usable ) {
- if( pConstraint->iColumn==-1 &&
- pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
- pInfo->idxNum = QUERY_ROWID; /* lookup by rowid */
- TRACE(("FTS1 QUERY_ROWID\n"));
- } else if( pConstraint->iColumn>=0 &&
- pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
- /* full-text search */
- pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn;
- TRACE(("FTS1 QUERY_FULLTEXT %d\n", pConstraint->iColumn));
- } else continue;
-
- pInfo->aConstraintUsage[i].argvIndex = 1;
- pInfo->aConstraintUsage[i].omit = 1;
-
- /* An arbitrary value for now.
- * TODO: Perhaps rowid matches should be considered cheaper than
- * full-text searches. */
- pInfo->estimatedCost = 1.0;
-
- return SQLITE_OK;
- }
- }
- pInfo->idxNum = QUERY_GENERIC;
- return SQLITE_OK;
-}
-
-static int fulltextDisconnect(sqlite3_vtab *pVTab){
- TRACE(("FTS1 Disconnect %p\n", pVTab));
- fulltext_vtab_destroy((fulltext_vtab *)pVTab);
- return SQLITE_OK;
-}
-
-static int fulltextDestroy(sqlite3_vtab *pVTab){
- fulltext_vtab *v = (fulltext_vtab *)pVTab;
- int rc;
-
- TRACE(("FTS1 Destroy %p\n", pVTab));
- rc = sql_exec(v->db, v->zDb, v->zName,
- "drop table if exists %_content;"
- "drop table if exists %_term;"
- );
- if( rc!=SQLITE_OK ) return rc;
-
- fulltext_vtab_destroy((fulltext_vtab *)pVTab);
- return SQLITE_OK;
-}
-
-static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
- fulltext_cursor *c;
-
- c = (fulltext_cursor *) calloc(sizeof(fulltext_cursor), 1);
- /* sqlite will initialize c->base */
- *ppCursor = &c->base;
- TRACE(("FTS1 Open %p: %p\n", pVTab, c));
-
- return SQLITE_OK;
-}
-
-
-/* Free all of the dynamically allocated memory held by *q
-*/
-static void queryClear(Query *q){
- int i;
- for(i = 0; i < q->nTerms; ++i){
- free(q->pTerms[i].pTerm);
- }
- free(q->pTerms);
- memset(q, 0, sizeof(*q));
-}
-
-/* Free all of the dynamically allocated memory held by the
-** Snippet
-*/
-static void snippetClear(Snippet *p){
- free(p->aMatch);
- free(p->zOffset);
- free(p->zSnippet);
- memset(p, 0, sizeof(*p));
-}
-/*
-** Append a single entry to the p->aMatch[] log.
-*/
-static void snippetAppendMatch(
- Snippet *p, /* Append the entry to this snippet */
- int iCol, int iTerm, /* The column and query term */
- int iStart, int nByte /* Offset and size of the match */
-){
- int i;
- struct snippetMatch *pMatch;
- if( p->nMatch+1>=p->nAlloc ){
- p->nAlloc = p->nAlloc*2 + 10;
- p->aMatch = realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
- if( p->aMatch==0 ){
- p->nMatch = 0;
- p->nAlloc = 0;
- return;
- }
- }
- i = p->nMatch++;
- pMatch = &p->aMatch[i];
- pMatch->iCol = iCol;
- pMatch->iTerm = iTerm;
- pMatch->iStart = iStart;
- pMatch->nByte = nByte;
-}
-
-/*
-** Sizing information for the circular buffer used in snippetOffsetsOfColumn()
-*/
-#define FTS1_ROTOR_SZ (32)
-#define FTS1_ROTOR_MASK (FTS1_ROTOR_SZ-1)
-
-/*
-** Add entries to pSnippet->aMatch[] for every match that occurs against
-** document zDoc[0..nDoc-1] which is stored in column iColumn.
-*/
-static void snippetOffsetsOfColumn(
- Query *pQuery,
- Snippet *pSnippet,
- int iColumn,
- const char *zDoc,
- int nDoc
-){
- const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */
- sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */
- sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */
- fulltext_vtab *pVtab; /* The full text index */
- int nColumn; /* Number of columns in the index */
- const QueryTerm *aTerm; /* Query string terms */
- int nTerm; /* Number of query string terms */
- int i, j; /* Loop counters */
- int rc; /* Return code */
- unsigned int match, prevMatch; /* Phrase search bitmasks */
- const char *zToken; /* Next token from the tokenizer */
- int nToken; /* Size of zToken */
- int iBegin, iEnd, iPos; /* Offsets of beginning and end */
-
- /* The following variables keep a circular buffer of the last
- ** few tokens */
- unsigned int iRotor = 0; /* Index of current token */
- int iRotorBegin[FTS1_ROTOR_SZ]; /* Beginning offset of token */
- int iRotorLen[FTS1_ROTOR_SZ]; /* Length of token */
-
- pVtab = pQuery->pFts;
- nColumn = pVtab->nColumn;
- pTokenizer = pVtab->pTokenizer;
- pTModule = pTokenizer->pModule;
- rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor);
- if( rc ) return;
- pTCursor->pTokenizer = pTokenizer;
- aTerm = pQuery->pTerms;
- nTerm = pQuery->nTerms;
- if( nTerm>=FTS1_ROTOR_SZ ){
- nTerm = FTS1_ROTOR_SZ - 1;
- }
- prevMatch = 0;
- while(1){
- rc = pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
- if( rc ) break;
- iRotorBegin[iRotor&FTS1_ROTOR_MASK] = iBegin;
- iRotorLen[iRotor&FTS1_ROTOR_MASK] = iEnd-iBegin;
- match = 0;
- for(i=0; i=0 && iCol1 && (prevMatch & (1<=0; j--){
- int k = (iRotor-j) & FTS1_ROTOR_MASK;
- snippetAppendMatch(pSnippet, iColumn, i-j,
- iRotorBegin[k], iRotorLen[k]);
- }
- }
- }
- prevMatch = match<<1;
- iRotor++;
- }
- pTModule->xClose(pTCursor);
-}
-
-
-/*
-** Compute all offsets for the current row of the query.
-** If the offsets have already been computed, this routine is a no-op.
-*/
-static void snippetAllOffsets(fulltext_cursor *p){
- int nColumn;
- int iColumn, i;
- int iFirst, iLast;
- fulltext_vtab *pFts;
-
- if( p->snippet.nMatch ) return;
- if( p->q.nTerms==0 ) return;
- pFts = p->q.pFts;
- nColumn = pFts->nColumn;
- iColumn = p->iCursorType - QUERY_FULLTEXT;
- if( iColumn<0 || iColumn>=nColumn ){
- iFirst = 0;
- iLast = nColumn-1;
- }else{
- iFirst = iColumn;
- iLast = iColumn;
- }
- for(i=iFirst; i<=iLast; i++){
- const char *zDoc;
- int nDoc;
- zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1);
- nDoc = sqlite3_column_bytes(p->pStmt, i+1);
- snippetOffsetsOfColumn(&p->q, &p->snippet, i, zDoc, nDoc);
- }
-}
-
-/*
-** Convert the information in the aMatch[] array of the snippet
-** into the string zOffset[0..nOffset-1].
-*/
-static void snippetOffsetText(Snippet *p){
- int i;
- int cnt = 0;
- StringBuffer sb;
- char zBuf[200];
- if( p->zOffset ) return;
- initStringBuffer(&sb);
- for(i=0; inMatch; i++){
- struct snippetMatch *pMatch = &p->aMatch[i];
- zBuf[0] = ' ';
- sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
- pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
- append(&sb, zBuf);
- cnt++;
- }
- p->zOffset = sb.s;
- p->nOffset = sb.len;
-}
-
-/*
-** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set
-** of matching words some of which might be in zDoc. zDoc is column
-** number iCol.
-**
-** iBreak is suggested spot in zDoc where we could begin or end an
-** excerpt. Return a value similar to iBreak but possibly adjusted
-** to be a little left or right so that the break point is better.
-*/
-static int wordBoundary(
- int iBreak, /* The suggested break point */
- const char *zDoc, /* Document text */
- int nDoc, /* Number of bytes in zDoc[] */
- struct snippetMatch *aMatch, /* Matching words */
- int nMatch, /* Number of entries in aMatch[] */
- int iCol /* The column number for zDoc[] */
-){
- int i;
- if( iBreak<=10 ){
- return 0;
- }
- if( iBreak>=nDoc-10 ){
- return nDoc;
- }
- for(i=0; i0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){
- return aMatch[i-1].iStart;
- }
- }
- for(i=1; i<=10; i++){
- if( safe_isspace(zDoc[iBreak-i]) ){
- return iBreak - i + 1;
- }
- if( safe_isspace(zDoc[iBreak+i]) ){
- return iBreak + i + 1;
- }
- }
- return iBreak;
-}
-
-/*
-** If the StringBuffer does not end in white space, add a single
-** space character to the end.
-*/
-static void appendWhiteSpace(StringBuffer *p){
- if( p->len==0 ) return;
- if( safe_isspace(p->s[p->len-1]) ) return;
- append(p, " ");
-}
-
-/*
-** Remove white space from teh end of the StringBuffer
-*/
-static void trimWhiteSpace(StringBuffer *p){
- while( p->len>0 && safe_isspace(p->s[p->len-1]) ){
- p->len--;
- }
-}
-
-
-
-/*
-** Allowed values for Snippet.aMatch[].snStatus
-*/
-#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */
-#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */
-
-/*
-** Generate the text of a snippet.
-*/
-static void snippetText(
- fulltext_cursor *pCursor, /* The cursor we need the snippet for */
- const char *zStartMark, /* Markup to appear before each match */
- const char *zEndMark, /* Markup to appear after each match */
- const char *zEllipsis /* Ellipsis mark */
-){
- int i, j;
- struct snippetMatch *aMatch;
- int nMatch;
- int nDesired;
- StringBuffer sb;
- int tailCol;
- int tailOffset;
- int iCol;
- int nDoc;
- const char *zDoc;
- int iStart, iEnd;
- int tailEllipsis = 0;
- int iMatch;
-
-
- free(pCursor->snippet.zSnippet);
- pCursor->snippet.zSnippet = 0;
- aMatch = pCursor->snippet.aMatch;
- nMatch = pCursor->snippet.nMatch;
- initStringBuffer(&sb);
-
- for(i=0; iq.nTerms; i++){
- for(j=0; j0; i++){
- if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue;
- nDesired--;
- iCol = aMatch[i].iCol;
- zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1);
- nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1);
- iStart = aMatch[i].iStart - 40;
- iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol);
- if( iStart<=10 ){
- iStart = 0;
- }
- if( iCol==tailCol && iStart<=tailOffset+20 ){
- iStart = tailOffset;
- }
- if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){
- trimWhiteSpace(&sb);
- appendWhiteSpace(&sb);
- append(&sb, zEllipsis);
- appendWhiteSpace(&sb);
- }
- iEnd = aMatch[i].iStart + aMatch[i].nByte + 40;
- iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol);
- if( iEnd>=nDoc-10 ){
- iEnd = nDoc;
- tailEllipsis = 0;
- }else{
- tailEllipsis = 1;
- }
- while( iMatchsnippet.zSnippet = sb.s;
- pCursor->snippet.nSnippet = sb.len;
-}
-
-
-/*
-** Close the cursor. For additional information see the documentation
-** on the xClose method of the virtual table interface.
-*/
-static int fulltextClose(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- TRACE(("FTS1 Close %p\n", c));
- sqlite3_finalize(c->pStmt);
- queryClear(&c->q);
- snippetClear(&c->snippet);
- if( c->result.pDoclist!=NULL ){
- docListDelete(c->result.pDoclist);
- }
- free(c);
- return SQLITE_OK;
-}
-
-static int fulltextNext(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- sqlite_int64 iDocid;
- int rc;
-
- TRACE(("FTS1 Next %p\n", pCursor));
- snippetClear(&c->snippet);
- if( c->iCursorType < QUERY_FULLTEXT ){
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
- rc = sqlite3_step(c->pStmt);
- switch( rc ){
- case SQLITE_ROW:
- c->eof = 0;
- return SQLITE_OK;
- case SQLITE_DONE:
- c->eof = 1;
- return SQLITE_OK;
- default:
- c->eof = 1;
- return rc;
- }
- } else { /* full-text query */
- rc = sqlite3_reset(c->pStmt);
- if( rc!=SQLITE_OK ) return rc;
-
- iDocid = nextDocid(&c->result);
- if( iDocid==0 ){
- c->eof = 1;
- return SQLITE_OK;
- }
- rc = sqlite3_bind_int64(c->pStmt, 1, iDocid);
- if( rc!=SQLITE_OK ) return rc;
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
- rc = sqlite3_step(c->pStmt);
- if( rc==SQLITE_ROW ){ /* the case we expect */
- c->eof = 0;
- return SQLITE_OK;
- }
- /* an error occurred; abort */
- return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
- }
-}
-
-
-/* Return a DocList corresponding to the query term *pTerm. If *pTerm
-** is the first term of a phrase query, go ahead and evaluate the phrase
-** query and return the doclist for the entire phrase query.
-**
-** The result is stored in pTerm->doclist.
-*/
-static int docListOfTerm(
- fulltext_vtab *v, /* The full text index */
- int iColumn, /* column to restrict to. No restrition if >=nColumn */
- QueryTerm *pQTerm, /* Term we are looking for, or 1st term of a phrase */
- DocList **ppResult /* Write the result here */
-){
- DocList *pLeft, *pRight, *pNew;
- int i, rc;
-
- pLeft = docListNew(DL_POSITIONS);
- rc = term_select_all(v, iColumn, pQTerm->pTerm, pQTerm->nTerm, pLeft);
- if( rc ){
- docListDelete(pLeft);
- return rc;
- }
- for(i=1; i<=pQTerm->nPhrase; i++){
- pRight = docListNew(DL_POSITIONS);
- rc = term_select_all(v, iColumn, pQTerm[i].pTerm, pQTerm[i].nTerm, pRight);
- if( rc ){
- docListDelete(pLeft);
- return rc;
- }
- pNew = docListNew(inPhrase ? DL_POSITIONS : DL_DOCIDS);
- docListPhraseMerge(pLeft, pRight, pNew);
- docListDelete(pLeft);
- docListDelete(pRight);
- pLeft = pNew;
- }
- *ppResult = pLeft;
- return SQLITE_OK;
-}
-
-/* Add a new term pTerm[0..nTerm-1] to the query *q.
-*/
-static void queryAdd(Query *q, const char *pTerm, int nTerm){
- QueryTerm *t;
- ++q->nTerms;
- q->pTerms = realloc(q->pTerms, q->nTerms * sizeof(q->pTerms[0]));
- if( q->pTerms==0 ){
- q->nTerms = 0;
- return;
- }
- t = &q->pTerms[q->nTerms - 1];
- memset(t, 0, sizeof(*t));
- t->pTerm = malloc(nTerm+1);
- memcpy(t->pTerm, pTerm, nTerm);
- t->pTerm[nTerm] = 0;
- t->nTerm = nTerm;
- t->isOr = q->nextIsOr;
- q->nextIsOr = 0;
- t->iColumn = q->nextColumn;
- q->nextColumn = q->dfltColumn;
-}
-
-/*
-** Check to see if the string zToken[0...nToken-1] matches any
-** column name in the virtual table. If it does,
-** return the zero-indexed column number. If not, return -1.
-*/
-static int checkColumnSpecifier(
- fulltext_vtab *pVtab, /* The virtual table */
- const char *zToken, /* Text of the token */
- int nToken /* Number of characters in the token */
-){
- int i;
- for(i=0; inColumn; i++){
- if( memcmp(pVtab->azColumn[i], zToken, nToken)==0
- && pVtab->azColumn[i][nToken]==0 ){
- return i;
- }
- }
- return -1;
-}
-
-/*
-** Parse the text at pSegment[0..nSegment-1]. Add additional terms
-** to the query being assemblied in pQuery.
-**
-** inPhrase is true if pSegment[0..nSegement-1] is contained within
-** double-quotes. If inPhrase is true, then the first term
-** is marked with the number of terms in the phrase less one and
-** OR and "-" syntax is ignored. If inPhrase is false, then every
-** term found is marked with nPhrase=0 and OR and "-" syntax is significant.
-*/
-static int tokenizeSegment(
- sqlite3_tokenizer *pTokenizer, /* The tokenizer to use */
- const char *pSegment, int nSegment, /* Query expression being parsed */
- int inPhrase, /* True if within "..." */
- Query *pQuery /* Append results here */
-){
- const sqlite3_tokenizer_module *pModule = pTokenizer->pModule;
- sqlite3_tokenizer_cursor *pCursor;
- int firstIndex = pQuery->nTerms;
- int iCol;
- int nTerm = 1;
-
- int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor);
- if( rc!=SQLITE_OK ) return rc;
- pCursor->pTokenizer = pTokenizer;
-
- while( 1 ){
- const char *pToken;
- int nToken, iBegin, iEnd, iPos;
-
- rc = pModule->xNext(pCursor,
- &pToken, &nToken,
- &iBegin, &iEnd, &iPos);
- if( rc!=SQLITE_OK ) break;
- if( !inPhrase &&
- pSegment[iEnd]==':' &&
- (iCol = checkColumnSpecifier(pQuery->pFts, pToken, nToken))>=0 ){
- pQuery->nextColumn = iCol;
- continue;
- }
- if( !inPhrase && pQuery->nTerms>0 && nToken==2
- && pSegment[iBegin]=='O' && pSegment[iBegin+1]=='R' ){
- pQuery->nextIsOr = 1;
- continue;
- }
- queryAdd(pQuery, pToken, nToken);
- if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){
- pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
- }
- pQuery->pTerms[pQuery->nTerms-1].iPhrase = nTerm;
- if( inPhrase ){
- nTerm++;
- }
- }
-
- if( inPhrase && pQuery->nTerms>firstIndex ){
- pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1;
- }
-
- return pModule->xClose(pCursor);
-}
-
-/* Parse a query string, yielding a Query object pQuery.
-**
-** The calling function will need to queryClear() to clean up
-** the dynamically allocated memory held by pQuery.
-*/
-static int parseQuery(
- fulltext_vtab *v, /* The fulltext index */
- const char *zInput, /* Input text of the query string */
- int nInput, /* Size of the input text */
- int dfltColumn, /* Default column of the index to match against */
- Query *pQuery /* Write the parse results here. */
-){
- int iInput, inPhrase = 0;
-
- if( zInput==0 ) nInput = 0;
- if( nInput<0 ) nInput = strlen(zInput);
- pQuery->nTerms = 0;
- pQuery->pTerms = NULL;
- pQuery->nextIsOr = 0;
- pQuery->nextColumn = dfltColumn;
- pQuery->dfltColumn = dfltColumn;
- pQuery->pFts = v;
-
- for(iInput=0; iInputiInput ){
- tokenizeSegment(v->pTokenizer, zInput+iInput, i-iInput, inPhrase,
- pQuery);
- }
- iInput = i;
- if( i=nColumn
-** they are allowed to match against any column.
-*/
-static int fulltextQuery(
- fulltext_vtab *v, /* The full text index */
- int iColumn, /* Match against this column by default */
- const char *zInput, /* The query string */
- int nInput, /* Number of bytes in zInput[] */
- DocList **pResult, /* Write the result doclist here */
- Query *pQuery /* Put parsed query string here */
-){
- int i, iNext, rc;
- DocList *pLeft = NULL;
- DocList *pRight, *pNew, *pOr;
- int nNot = 0;
- QueryTerm *aTerm;
-
- rc = parseQuery(v, zInput, nInput, iColumn, pQuery);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Merge AND terms. */
- aTerm = pQuery->pTerms;
- for(i = 0; inTerms; i=iNext){
- if( aTerm[i].isNot ){
- /* Handle all NOT terms in a separate pass */
- nNot++;
- iNext = i + aTerm[i].nPhrase+1;
- continue;
- }
- iNext = i + aTerm[i].nPhrase + 1;
- rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &pRight);
- if( rc ){
- queryClear(pQuery);
- return rc;
- }
- while( iNextnTerms && aTerm[iNext].isOr ){
- rc = docListOfTerm(v, aTerm[iNext].iColumn, &aTerm[iNext], &pOr);
- iNext += aTerm[iNext].nPhrase + 1;
- if( rc ){
- queryClear(pQuery);
- return rc;
- }
- pNew = docListNew(DL_DOCIDS);
- docListOrMerge(pRight, pOr, pNew);
- docListDelete(pRight);
- docListDelete(pOr);
- pRight = pNew;
- }
- if( pLeft==0 ){
- pLeft = pRight;
- }else{
- pNew = docListNew(DL_DOCIDS);
- docListAndMerge(pLeft, pRight, pNew);
- docListDelete(pRight);
- docListDelete(pLeft);
- pLeft = pNew;
- }
- }
-
- if( nNot && pLeft==0 ){
- /* We do not yet know how to handle a query of only NOT terms */
- return SQLITE_ERROR;
- }
-
- /* Do the EXCEPT terms */
- for(i=0; inTerms; i += aTerm[i].nPhrase + 1){
- if( !aTerm[i].isNot ) continue;
- rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &pRight);
- if( rc ){
- queryClear(pQuery);
- docListDelete(pLeft);
- return rc;
- }
- pNew = docListNew(DL_DOCIDS);
- docListExceptMerge(pLeft, pRight, pNew);
- docListDelete(pRight);
- docListDelete(pLeft);
- pLeft = pNew;
- }
-
- *pResult = pLeft;
- return rc;
-}
-
-/*
-** This is the xFilter interface for the virtual table. See
-** the virtual table xFilter method documentation for additional
-** information.
-**
-** If idxNum==QUERY_GENERIC then do a full table scan against
-** the %_content table.
-**
-** If idxNum==QUERY_ROWID then do a rowid lookup for a single entry
-** in the %_content table.
-**
-** If idxNum>=QUERY_FULLTEXT then use the full text index. The
-** column on the left-hand side of the MATCH operator is column
-** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand
-** side of the MATCH operator.
-*/
-/* TODO(shess) Upgrade the cursor initialization and destruction to
-** account for fulltextFilter() being called multiple times on the
-** same cursor. The current solution is very fragile. Apply fix to
-** fts2 as appropriate.
-*/
-static int fulltextFilter(
- sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
- int idxNum, const char *idxStr, /* Which indexing scheme to use */
- int argc, sqlite3_value **argv /* Arguments for the indexing scheme */
-){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- fulltext_vtab *v = cursor_vtab(c);
- int rc;
- char *zSql;
-
- TRACE(("FTS1 Filter %p\n",pCursor));
-
- zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
- idxNum==QUERY_GENERIC ? "" : "where rowid=?");
- sqlite3_finalize(c->pStmt);
- rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
- sqlite3_free(zSql);
- if( rc!=SQLITE_OK ) return rc;
-
- c->iCursorType = idxNum;
- switch( idxNum ){
- case QUERY_GENERIC:
- break;
-
- case QUERY_ROWID:
- rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0]));
- if( rc!=SQLITE_OK ) return rc;
- break;
-
- default: /* full-text search */
- {
- const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
- DocList *pResult;
- assert( idxNum<=QUERY_FULLTEXT+v->nColumn);
- assert( argc==1 );
- queryClear(&c->q);
- rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &pResult, &c->q);
- if( rc!=SQLITE_OK ) return rc;
- if( c->result.pDoclist!=NULL ) docListDelete(c->result.pDoclist);
- readerInit(&c->result, pResult);
- break;
- }
- }
-
- return fulltextNext(pCursor);
-}
-
-/* This is the xEof method of the virtual table. The SQLite core
-** calls this routine to find out if it has reached the end of
-** a query's results set.
-*/
-static int fulltextEof(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- return c->eof;
-}
-
-/* This is the xColumn method of the virtual table. The SQLite
-** core calls this method during a query when it needs the value
-** of a column from the virtual table. This method needs to use
-** one of the sqlite3_result_*() routines to store the requested
-** value back in the pContext.
-*/
-static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
- sqlite3_context *pContext, int idxCol){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- fulltext_vtab *v = cursor_vtab(c);
-
- if( idxColnColumn ){
- sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
- sqlite3_result_value(pContext, pVal);
- }else if( idxCol==v->nColumn ){
- /* The extra column whose name is the same as the table.
- ** Return a blob which is a pointer to the cursor
- */
- sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
- }
- return SQLITE_OK;
-}
-
-/* This is the xRowid method. The SQLite core calls this routine to
-** retrive the rowid for the current row of the result set. The
-** rowid should be written to *pRowid.
-*/
-static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
-
- *pRowid = sqlite3_column_int64(c->pStmt, 0);
- return SQLITE_OK;
-}
-
-/* Add all terms in [zText] to the given hash table. If [iColumn] > 0,
- * we also store positions and offsets in the hash table using the given
- * column number. */
-static int buildTerms(fulltext_vtab *v, fts1Hash *terms, sqlite_int64 iDocid,
- const char *zText, int iColumn){
- sqlite3_tokenizer *pTokenizer = v->pTokenizer;
- sqlite3_tokenizer_cursor *pCursor;
- const char *pToken;
- int nTokenBytes;
- int iStartOffset, iEndOffset, iPosition;
- int rc;
-
- rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor);
- if( rc!=SQLITE_OK ) return rc;
-
- pCursor->pTokenizer = pTokenizer;
- while( SQLITE_OK==pTokenizer->pModule->xNext(pCursor,
- &pToken, &nTokenBytes,
- &iStartOffset, &iEndOffset,
- &iPosition) ){
- DocList *p;
-
- /* Positions can't be negative; we use -1 as a terminator internally. */
- if( iPosition<0 ){
- pTokenizer->pModule->xClose(pCursor);
- return SQLITE_ERROR;
- }
-
- p = fts1HashFind(terms, pToken, nTokenBytes);
- if( p==NULL ){
- p = docListNew(DL_DEFAULT);
- docListAddDocid(p, iDocid);
- fts1HashInsert(terms, pToken, nTokenBytes, p);
- }
- if( iColumn>=0 ){
- docListAddPosOffset(p, iColumn, iPosition, iStartOffset, iEndOffset);
- }
- }
-
- /* TODO(shess) Check return? Should this be able to cause errors at
- ** this point? Actually, same question about sqlite3_finalize(),
- ** though one could argue that failure there means that the data is
- ** not durable. *ponder*
- */
- pTokenizer->pModule->xClose(pCursor);
- return rc;
-}
-
-/* Update the %_terms table to map the term [pTerm] to the given rowid. */
-static int index_insert_term(fulltext_vtab *v, const char *pTerm, int nTerm,
- DocList *d){
- sqlite_int64 iIndexRow;
- DocList doclist;
- int iSegment = 0, rc;
-
- rc = term_select(v, pTerm, nTerm, iSegment, &iIndexRow, &doclist);
- if( rc==SQLITE_DONE ){
- docListInit(&doclist, DL_DEFAULT, 0, 0);
- docListUpdate(&doclist, d);
- /* TODO(shess) Consider length(doclist)>CHUNK_MAX? */
- rc = term_insert(v, NULL, pTerm, nTerm, iSegment, &doclist);
- goto err;
- }
- if( rc!=SQLITE_ROW ) return SQLITE_ERROR;
-
- docListUpdate(&doclist, d);
- if( doclist.nData<=CHUNK_MAX ){
- rc = term_update(v, iIndexRow, &doclist);
- goto err;
- }
-
- /* Doclist doesn't fit, delete what's there, and accumulate
- ** forward.
- */
- rc = term_delete(v, iIndexRow);
- if( rc!=SQLITE_OK ) goto err;
-
- /* Try to insert the doclist into a higher segment bucket. On
- ** failure, accumulate existing doclist with the doclist from that
- ** bucket, and put results in the next bucket.
- */
- iSegment++;
- while( (rc=term_insert(v, &iIndexRow, pTerm, nTerm, iSegment,
- &doclist))!=SQLITE_OK ){
- sqlite_int64 iSegmentRow;
- DocList old;
- int rc2;
-
- /* Retain old error in case the term_insert() error was really an
- ** error rather than a bounced insert.
- */
- rc2 = term_select(v, pTerm, nTerm, iSegment, &iSegmentRow, &old);
- if( rc2!=SQLITE_ROW ) goto err;
-
- rc = term_delete(v, iSegmentRow);
- if( rc!=SQLITE_OK ) goto err;
-
- /* Reusing lowest-number deleted row keeps the index smaller. */
- if( iSegmentRownColumn ; ++i){
- char *zText = (char*)sqlite3_value_text(pValues[i]);
- int rc = buildTerms(v, terms, iRowid, zText, i);
- if( rc!=SQLITE_OK ) return rc;
- }
- return SQLITE_OK;
-}
-
-/* Add empty doclists for all terms in the given row's content to the hash
- * table [pTerms]. */
-static int deleteTerms(fulltext_vtab *v, fts1Hash *pTerms, sqlite_int64 iRowid){
- const char **pValues;
- int i;
-
- int rc = content_select(v, iRowid, &pValues);
- if( rc!=SQLITE_OK ) return rc;
-
- for(i = 0 ; i < v->nColumn; ++i) {
- rc = buildTerms(v, pTerms, iRowid, pValues[i], -1);
- if( rc!=SQLITE_OK ) break;
- }
-
- freeStringArray(v->nColumn, pValues);
- return SQLITE_OK;
-}
-
-/* Insert a row into the %_content table; set *piRowid to be the ID of the
- * new row. Fill [pTerms] with new doclists for the %_term table. */
-static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestRowid,
- sqlite3_value **pValues,
- sqlite_int64 *piRowid, fts1Hash *pTerms){
- int rc;
-
- rc = content_insert(v, pRequestRowid, pValues); /* execute an SQL INSERT */
- if( rc!=SQLITE_OK ) return rc;
- *piRowid = sqlite3_last_insert_rowid(v->db);
- return insertTerms(v, pTerms, *piRowid, pValues);
-}
-
-/* Delete a row from the %_content table; fill [pTerms] with empty doclists
- * to be written to the %_term table. */
-static int index_delete(fulltext_vtab *v, sqlite_int64 iRow, fts1Hash *pTerms){
- int rc = deleteTerms(v, pTerms, iRow);
- if( rc!=SQLITE_OK ) return rc;
- return content_delete(v, iRow); /* execute an SQL DELETE */
-}
-
-/* Update a row in the %_content table; fill [pTerms] with new doclists for the
- * %_term table. */
-static int index_update(fulltext_vtab *v, sqlite_int64 iRow,
- sqlite3_value **pValues, fts1Hash *pTerms){
- /* Generate an empty doclist for each term that previously appeared in this
- * row. */
- int rc = deleteTerms(v, pTerms, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */
- if( rc!=SQLITE_OK ) return rc;
-
- /* Now add positions for terms which appear in the updated row. */
- return insertTerms(v, pTerms, iRow, pValues);
-}
-
-/* This function implements the xUpdate callback; it is the top-level entry
- * point for inserting, deleting or updating a row in a full-text table. */
-static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
- sqlite_int64 *pRowid){
- fulltext_vtab *v = (fulltext_vtab *) pVtab;
- fts1Hash terms; /* maps term string -> PosList */
- int rc;
- fts1HashElem *e;
-
- TRACE(("FTS1 Update %p\n", pVtab));
-
- fts1HashInit(&terms, FTS1_HASH_STRING, 1);
-
- if( nArg<2 ){
- rc = index_delete(v, sqlite3_value_int64(ppArg[0]), &terms);
- } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
- /* An update:
- * ppArg[0] = old rowid
- * ppArg[1] = new rowid
- * ppArg[2..2+v->nColumn-1] = values
- * ppArg[2+v->nColumn] = value for magic column (we ignore this)
- */
- sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]);
- if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER ||
- sqlite3_value_int64(ppArg[1]) != rowid ){
- rc = SQLITE_ERROR; /* we don't allow changing the rowid */
- } else {
- assert( nArg==2+v->nColumn+1);
- rc = index_update(v, rowid, &ppArg[2], &terms);
- }
- } else {
- /* An insert:
- * ppArg[1] = requested rowid
- * ppArg[2..2+v->nColumn-1] = values
- * ppArg[2+v->nColumn] = value for magic column (we ignore this)
- */
- assert( nArg==2+v->nColumn+1);
- rc = index_insert(v, ppArg[1], &ppArg[2], pRowid, &terms);
- }
-
- if( rc==SQLITE_OK ){
- /* Write updated doclists to disk. */
- for(e=fts1HashFirst(&terms); e; e=fts1HashNext(e)){
- DocList *p = fts1HashData(e);
- rc = index_insert_term(v, fts1HashKey(e), fts1HashKeysize(e), p);
- if( rc!=SQLITE_OK ) break;
- }
- }
-
- /* clean up */
- for(e=fts1HashFirst(&terms); e; e=fts1HashNext(e)){
- DocList *p = fts1HashData(e);
- docListDelete(p);
- }
- fts1HashClear(&terms);
-
- return rc;
-}
-
-/*
-** Implementation of the snippet() function for FTS1
-*/
-static void snippetFunc(
- sqlite3_context *pContext,
- int argc,
- sqlite3_value **argv
-){
- fulltext_cursor *pCursor;
- if( argc<1 ) return;
- if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
- sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
- sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1);
- }else{
- const char *zStart = "";
- const char *zEnd = "";
- const char *zEllipsis = "...";
- memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
- if( argc>=2 ){
- zStart = (const char*)sqlite3_value_text(argv[1]);
- if( argc>=3 ){
- zEnd = (const char*)sqlite3_value_text(argv[2]);
- if( argc>=4 ){
- zEllipsis = (const char*)sqlite3_value_text(argv[3]);
- }
- }
- }
- snippetAllOffsets(pCursor);
- snippetText(pCursor, zStart, zEnd, zEllipsis);
- sqlite3_result_text(pContext, pCursor->snippet.zSnippet,
- pCursor->snippet.nSnippet, SQLITE_STATIC);
- }
-}
-
-/*
-** Implementation of the offsets() function for FTS1
-*/
-static void snippetOffsetsFunc(
- sqlite3_context *pContext,
- int argc,
- sqlite3_value **argv
-){
- fulltext_cursor *pCursor;
- if( argc<1 ) return;
- if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
- sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
- sqlite3_result_error(pContext, "illegal first argument to offsets",-1);
- }else{
- memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
- snippetAllOffsets(pCursor);
- snippetOffsetText(&pCursor->snippet);
- sqlite3_result_text(pContext,
- pCursor->snippet.zOffset, pCursor->snippet.nOffset,
- SQLITE_STATIC);
- }
-}
-
-/*
-** This routine implements the xFindFunction method for the FTS1
-** virtual table.
-*/
-static int fulltextFindFunction(
- sqlite3_vtab *pVtab,
- int nArg,
- const char *zName,
- void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
- void **ppArg
-){
- if( strcmp(zName,"snippet")==0 ){
- *pxFunc = snippetFunc;
- return 1;
- }else if( strcmp(zName,"offsets")==0 ){
- *pxFunc = snippetOffsetsFunc;
- return 1;
- }
- return 0;
-}
-
-/*
-** Rename an fts1 table.
-*/
-static int fulltextRename(
- sqlite3_vtab *pVtab,
- const char *zName
-){
- fulltext_vtab *p = (fulltext_vtab *)pVtab;
- int rc = SQLITE_NOMEM;
- char *zSql = sqlite3_mprintf(
- "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';"
- "ALTER TABLE %Q.'%q_term' RENAME TO '%q_term';"
- , p->zDb, p->zName, zName
- , p->zDb, p->zName, zName
- );
- if( zSql ){
- rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
- sqlite3_free(zSql);
- }
- return rc;
-}
-
-static const sqlite3_module fulltextModule = {
- /* iVersion */ 0,
- /* xCreate */ fulltextCreate,
- /* xConnect */ fulltextConnect,
- /* xBestIndex */ fulltextBestIndex,
- /* xDisconnect */ fulltextDisconnect,
- /* xDestroy */ fulltextDestroy,
- /* xOpen */ fulltextOpen,
- /* xClose */ fulltextClose,
- /* xFilter */ fulltextFilter,
- /* xNext */ fulltextNext,
- /* xEof */ fulltextEof,
- /* xColumn */ fulltextColumn,
- /* xRowid */ fulltextRowid,
- /* xUpdate */ fulltextUpdate,
- /* xBegin */ 0,
- /* xSync */ 0,
- /* xCommit */ 0,
- /* xRollback */ 0,
- /* xFindFunction */ fulltextFindFunction,
- /* xRename */ fulltextRename,
-};
-
-int sqlite3Fts1Init(sqlite3 *db){
- sqlite3_overload_function(db, "snippet", -1);
- sqlite3_overload_function(db, "offsets", -1);
- return sqlite3_create_module(db, "fts1", &fulltextModule, 0);
-}
-
-#if !SQLITE_CORE
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-int sqlite3_fts1_init(sqlite3 *db, char **pzErrMsg,
- const sqlite3_api_routines *pApi){
- SQLITE_EXTENSION_INIT2(pApi)
- return sqlite3Fts1Init(db);
-}
-#endif
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/sqlite/ext/fts1/fts1.h b/sqlite/ext/fts1/fts1.h
deleted file mode 100644
index d55e6897..00000000
--- a/sqlite/ext/fts1/fts1.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "sqlite3.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-int sqlite3Fts1Init(sqlite3 *db);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif /* __cplusplus */
diff --git a/sqlite/ext/fts1/fts1_hash.c b/sqlite/ext/fts1/fts1_hash.c
deleted file mode 100644
index 463a52b6..00000000
--- a/sqlite/ext/fts1/fts1_hash.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the implementation of generic hash-tables used in SQLite.
-** We've modified it slightly to serve as a standalone hash table
-** implementation for the full-text indexing module.
-*/
-#include
-#include
-#include
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS1 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS1 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
-
-
-#include "fts1_hash.h"
-
-static void *malloc_and_zero(int n){
- void *p = malloc(n);
- if( p ){
- memset(p, 0, n);
- }
- return p;
-}
-
-/* Turn bulk memory into a hash table object by initializing the
-** fields of the Hash structure.
-**
-** "pNew" is a pointer to the hash table that is to be initialized.
-** keyClass is one of the constants
-** FTS1_HASH_BINARY or FTS1_HASH_STRING. The value of keyClass
-** determines what kind of key the hash table will use. "copyKey" is
-** true if the hash table should make its own private copy of keys and
-** false if it should just use the supplied pointer.
-*/
-void sqlite3Fts1HashInit(fts1Hash *pNew, int keyClass, int copyKey){
- assert( pNew!=0 );
- assert( keyClass>=FTS1_HASH_STRING && keyClass<=FTS1_HASH_BINARY );
- pNew->keyClass = keyClass;
- pNew->copyKey = copyKey;
- pNew->first = 0;
- pNew->count = 0;
- pNew->htsize = 0;
- pNew->ht = 0;
- pNew->xMalloc = malloc_and_zero;
- pNew->xFree = free;
-}
-
-/* Remove all entries from a hash table. Reclaim all memory.
-** Call this routine to delete a hash table or to reset a hash table
-** to the empty state.
-*/
-void sqlite3Fts1HashClear(fts1Hash *pH){
- fts1HashElem *elem; /* For looping over all elements of the table */
-
- assert( pH!=0 );
- elem = pH->first;
- pH->first = 0;
- if( pH->ht ) pH->xFree(pH->ht);
- pH->ht = 0;
- pH->htsize = 0;
- while( elem ){
- fts1HashElem *next_elem = elem->next;
- if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
- }
- pH->xFree(elem);
- elem = next_elem;
- }
- pH->count = 0;
-}
-
-/*
-** Hash and comparison functions when the mode is FTS1_HASH_STRING
-*/
-static int strHash(const void *pKey, int nKey){
- const char *z = (const char *)pKey;
- int h = 0;
- if( nKey<=0 ) nKey = (int) strlen(z);
- while( nKey > 0 ){
- h = (h<<3) ^ h ^ *z++;
- nKey--;
- }
- return h & 0x7fffffff;
-}
-static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( n1!=n2 ) return 1;
- return strncmp((const char*)pKey1,(const char*)pKey2,n1);
-}
-
-/*
-** Hash and comparison functions when the mode is FTS1_HASH_BINARY
-*/
-static int binHash(const void *pKey, int nKey){
- int h = 0;
- const char *z = (const char *)pKey;
- while( nKey-- > 0 ){
- h = (h<<3) ^ h ^ *(z++);
- }
- return h & 0x7fffffff;
-}
-static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( n1!=n2 ) return 1;
- return memcmp(pKey1,pKey2,n1);
-}
-
-/*
-** Return a pointer to the appropriate hash function given the key class.
-**
-** The C syntax in this function definition may be unfamilar to some
-** programmers, so we provide the following additional explanation:
-**
-** The name of the function is "hashFunction". The function takes a
-** single parameter "keyClass". The return value of hashFunction()
-** is a pointer to another function. Specifically, the return value
-** of hashFunction() is a pointer to a function that takes two parameters
-** with types "const void*" and "int" and returns an "int".
-*/
-static int (*hashFunction(int keyClass))(const void*,int){
- if( keyClass==FTS1_HASH_STRING ){
- return &strHash;
- }else{
- assert( keyClass==FTS1_HASH_BINARY );
- return &binHash;
- }
-}
-
-/*
-** Return a pointer to the appropriate hash function given the key class.
-**
-** For help in interpreted the obscure C code in the function definition,
-** see the header comment on the previous function.
-*/
-static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
- if( keyClass==FTS1_HASH_STRING ){
- return &strCompare;
- }else{
- assert( keyClass==FTS1_HASH_BINARY );
- return &binCompare;
- }
-}
-
-/* Link an element into the hash table
-*/
-static void insertElement(
- fts1Hash *pH, /* The complete hash table */
- struct _fts1ht *pEntry, /* The entry into which pNew is inserted */
- fts1HashElem *pNew /* The element to be inserted */
-){
- fts1HashElem *pHead; /* First element already in pEntry */
- pHead = pEntry->chain;
- if( pHead ){
- pNew->next = pHead;
- pNew->prev = pHead->prev;
- if( pHead->prev ){ pHead->prev->next = pNew; }
- else { pH->first = pNew; }
- pHead->prev = pNew;
- }else{
- pNew->next = pH->first;
- if( pH->first ){ pH->first->prev = pNew; }
- pNew->prev = 0;
- pH->first = pNew;
- }
- pEntry->count++;
- pEntry->chain = pNew;
-}
-
-
-/* Resize the hash table so that it cantains "new_size" buckets.
-** "new_size" must be a power of 2. The hash table might fail
-** to resize if sqliteMalloc() fails.
-*/
-static void rehash(fts1Hash *pH, int new_size){
- struct _fts1ht *new_ht; /* The new hash table */
- fts1HashElem *elem, *next_elem; /* For looping over existing elements */
- int (*xHash)(const void*,int); /* The hash function */
-
- assert( (new_size & (new_size-1))==0 );
- new_ht = (struct _fts1ht *)pH->xMalloc( new_size*sizeof(struct _fts1ht) );
- if( new_ht==0 ) return;
- if( pH->ht ) pH->xFree(pH->ht);
- pH->ht = new_ht;
- pH->htsize = new_size;
- xHash = hashFunction(pH->keyClass);
- for(elem=pH->first, pH->first=0; elem; elem = next_elem){
- int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
- next_elem = elem->next;
- insertElement(pH, &new_ht[h], elem);
- }
-}
-
-/* This function (for internal use only) locates an element in an
-** hash table that matches the given key. The hash for this key has
-** already been computed and is passed as the 4th parameter.
-*/
-static fts1HashElem *findElementGivenHash(
- const fts1Hash *pH, /* The pH to be searched */
- const void *pKey, /* The key we are searching for */
- int nKey,
- int h /* The hash for this key. */
-){
- fts1HashElem *elem; /* Used to loop thru the element list */
- int count; /* Number of elements left to test */
- int (*xCompare)(const void*,int,const void*,int); /* comparison function */
-
- if( pH->ht ){
- struct _fts1ht *pEntry = &pH->ht[h];
- elem = pEntry->chain;
- count = pEntry->count;
- xCompare = compareFunction(pH->keyClass);
- while( count-- && elem ){
- if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
- return elem;
- }
- elem = elem->next;
- }
- }
- return 0;
-}
-
-/* Remove a single entry from the hash table given a pointer to that
-** element and a hash on the element's key.
-*/
-static void removeElementGivenHash(
- fts1Hash *pH, /* The pH containing "elem" */
- fts1HashElem* elem, /* The element to be removed from the pH */
- int h /* Hash value for the element */
-){
- struct _fts1ht *pEntry;
- if( elem->prev ){
- elem->prev->next = elem->next;
- }else{
- pH->first = elem->next;
- }
- if( elem->next ){
- elem->next->prev = elem->prev;
- }
- pEntry = &pH->ht[h];
- if( pEntry->chain==elem ){
- pEntry->chain = elem->next;
- }
- pEntry->count--;
- if( pEntry->count<=0 ){
- pEntry->chain = 0;
- }
- if( pH->copyKey && elem->pKey ){
- pH->xFree(elem->pKey);
- }
- pH->xFree( elem );
- pH->count--;
- if( pH->count<=0 ){
- assert( pH->first==0 );
- assert( pH->count==0 );
- fts1HashClear(pH);
- }
-}
-
-/* Attempt to locate an element of the hash table pH with a key
-** that matches pKey,nKey. Return the data for this element if it is
-** found, or NULL if there is no match.
-*/
-void *sqlite3Fts1HashFind(const fts1Hash *pH, const void *pKey, int nKey){
- int h; /* A hash on key */
- fts1HashElem *elem; /* The element that matches key */
- int (*xHash)(const void*,int); /* The hash function */
-
- if( pH==0 || pH->ht==0 ) return 0;
- xHash = hashFunction(pH->keyClass);
- assert( xHash!=0 );
- h = (*xHash)(pKey,nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
- return elem ? elem->data : 0;
-}
-
-/* Insert an element into the hash table pH. The key is pKey,nKey
-** and the data is "data".
-**
-** If no element exists with a matching key, then a new
-** element is created. A copy of the key is made if the copyKey
-** flag is set. NULL is returned.
-**
-** If another element already exists with the same key, then the
-** new data replaces the old data and the old data is returned.
-** The key is not copied in this instance. If a malloc fails, then
-** the new data is returned and the hash table is unchanged.
-**
-** If the "data" parameter to this function is NULL, then the
-** element corresponding to "key" is removed from the hash table.
-*/
-void *sqlite3Fts1HashInsert(
- fts1Hash *pH, /* The hash table to insert into */
- const void *pKey, /* The key */
- int nKey, /* Number of bytes in the key */
- void *data /* The data */
-){
- int hraw; /* Raw hash value of the key */
- int h; /* the hash of the key modulo hash table size */
- fts1HashElem *elem; /* Used to loop thru the element list */
- fts1HashElem *new_elem; /* New element added to the pH */
- int (*xHash)(const void*,int); /* The hash function */
-
- assert( pH!=0 );
- xHash = hashFunction(pH->keyClass);
- assert( xHash!=0 );
- hraw = (*xHash)(pKey, nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- elem = findElementGivenHash(pH,pKey,nKey,h);
- if( elem ){
- void *old_data = elem->data;
- if( data==0 ){
- removeElementGivenHash(pH,elem,h);
- }else{
- elem->data = data;
- }
- return old_data;
- }
- if( data==0 ) return 0;
- new_elem = (fts1HashElem*)pH->xMalloc( sizeof(fts1HashElem) );
- if( new_elem==0 ) return data;
- if( pH->copyKey && pKey!=0 ){
- new_elem->pKey = pH->xMalloc( nKey );
- if( new_elem->pKey==0 ){
- pH->xFree(new_elem);
- return data;
- }
- memcpy((void*)new_elem->pKey, pKey, nKey);
- }else{
- new_elem->pKey = (void*)pKey;
- }
- new_elem->nKey = nKey;
- pH->count++;
- if( pH->htsize==0 ){
- rehash(pH,8);
- if( pH->htsize==0 ){
- pH->count = 0;
- pH->xFree(new_elem);
- return data;
- }
- }
- if( pH->count > pH->htsize ){
- rehash(pH,pH->htsize*2);
- }
- assert( pH->htsize>0 );
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- insertElement(pH, &pH->ht[h], new_elem);
- new_elem->data = data;
- return 0;
-}
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/sqlite/ext/fts1/fts1_hash.h b/sqlite/ext/fts1/fts1_hash.h
deleted file mode 100644
index 90011529..00000000
--- a/sqlite/ext/fts1/fts1_hash.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the header file for the generic hash-table implementation
-** used in SQLite. We've modified it slightly to serve as a standalone
-** hash table implementation for the full-text indexing module.
-**
-*/
-#ifndef _FTS1_HASH_H_
-#define _FTS1_HASH_H_
-
-/* Forward declarations of structures. */
-typedef struct fts1Hash fts1Hash;
-typedef struct fts1HashElem fts1HashElem;
-
-/* A complete hash table is an instance of the following structure.
-** The internals of this structure are intended to be opaque -- client
-** code should not attempt to access or modify the fields of this structure
-** directly. Change this structure only by using the routines below.
-** However, many of the "procedures" and "functions" for modifying and
-** accessing this structure are really macros, so we can't really make
-** this structure opaque.
-*/
-struct fts1Hash {
- char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
- char copyKey; /* True if copy of key made on insert */
- int count; /* Number of entries in this table */
- fts1HashElem *first; /* The first element of the array */
- void *(*xMalloc)(int); /* malloc() function to use */
- void (*xFree)(void *); /* free() function to use */
- int htsize; /* Number of buckets in the hash table */
- struct _fts1ht { /* the hash table */
- int count; /* Number of entries with this hash */
- fts1HashElem *chain; /* Pointer to first entry with this hash */
- } *ht;
-};
-
-/* Each element in the hash table is an instance of the following
-** structure. All elements are stored on a single doubly-linked list.
-**
-** Again, this structure is intended to be opaque, but it can't really
-** be opaque because it is used by macros.
-*/
-struct fts1HashElem {
- fts1HashElem *next, *prev; /* Next and previous elements in the table */
- void *data; /* Data associated with this element */
- void *pKey; int nKey; /* Key associated with this element */
-};
-
-/*
-** There are 2 different modes of operation for a hash table:
-**
-** FTS1_HASH_STRING pKey points to a string that is nKey bytes long
-** (including the null-terminator, if any). Case
-** is respected in comparisons.
-**
-** FTS1_HASH_BINARY pKey points to binary data nKey bytes long.
-** memcmp() is used to compare keys.
-**
-** A copy of the key is made if the copyKey parameter to fts1HashInit is 1.
-*/
-#define FTS1_HASH_STRING 1
-#define FTS1_HASH_BINARY 2
-
-/*
-** Access routines. To delete, insert a NULL pointer.
-*/
-void sqlite3Fts1HashInit(fts1Hash*, int keytype, int copyKey);
-void *sqlite3Fts1HashInsert(fts1Hash*, const void *pKey, int nKey, void *pData);
-void *sqlite3Fts1HashFind(const fts1Hash*, const void *pKey, int nKey);
-void sqlite3Fts1HashClear(fts1Hash*);
-
-/*
-** Shorthand for the functions above
-*/
-#define fts1HashInit sqlite3Fts1HashInit
-#define fts1HashInsert sqlite3Fts1HashInsert
-#define fts1HashFind sqlite3Fts1HashFind
-#define fts1HashClear sqlite3Fts1HashClear
-
-/*
-** Macros for looping over all elements of a hash table. The idiom is
-** like this:
-**
-** fts1Hash h;
-** fts1HashElem *p;
-** ...
-** for(p=fts1HashFirst(&h); p; p=fts1HashNext(p)){
-** SomeStructure *pData = fts1HashData(p);
-** // do something with pData
-** }
-*/
-#define fts1HashFirst(H) ((H)->first)
-#define fts1HashNext(E) ((E)->next)
-#define fts1HashData(E) ((E)->data)
-#define fts1HashKey(E) ((E)->pKey)
-#define fts1HashKeysize(E) ((E)->nKey)
-
-/*
-** Number of entries in a hash table
-*/
-#define fts1HashCount(H) ((H)->count)
-
-#endif /* _FTS1_HASH_H_ */
diff --git a/sqlite/ext/fts1/fts1_porter.c b/sqlite/ext/fts1/fts1_porter.c
deleted file mode 100644
index 1d262366..00000000
--- a/sqlite/ext/fts1/fts1_porter.c
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
-** 2006 September 30
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** Implementation of the full-text-search tokenizer that implements
-** a Porter stemmer.
-*/
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS1 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS1 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
-
-
-#include
-#include
-#include
-#include
-#include
-
-#include "fts1_tokenizer.h"
-
-/*
-** Class derived from sqlite3_tokenizer
-*/
-typedef struct porter_tokenizer {
- sqlite3_tokenizer base; /* Base class */
-} porter_tokenizer;
-
-/*
-** Class derived from sqlit3_tokenizer_cursor
-*/
-typedef struct porter_tokenizer_cursor {
- sqlite3_tokenizer_cursor base;
- const char *zInput; /* input we are tokenizing */
- int nInput; /* size of the input */
- int iOffset; /* current position in zInput */
- int iToken; /* index of next token to be returned */
- char *zToken; /* storage for current token */
- int nAllocated; /* space allocated to zToken buffer */
-} porter_tokenizer_cursor;
-
-
-/* Forward declaration */
-static const sqlite3_tokenizer_module porterTokenizerModule;
-
-
-/*
-** Create a new tokenizer instance.
-*/
-static int porterCreate(
- int argc, const char * const *argv,
- sqlite3_tokenizer **ppTokenizer
-){
- porter_tokenizer *t;
- t = (porter_tokenizer *) calloc(sizeof(*t), 1);
- if( t==NULL ) return SQLITE_NOMEM;
-
- *ppTokenizer = &t->base;
- return SQLITE_OK;
-}
-
-/*
-** Destroy a tokenizer
-*/
-static int porterDestroy(sqlite3_tokenizer *pTokenizer){
- free(pTokenizer);
- return SQLITE_OK;
-}
-
-/*
-** Prepare to begin tokenizing a particular string. The input
-** string to be tokenized is zInput[0..nInput-1]. A cursor
-** used to incrementally tokenize this string is returned in
-** *ppCursor.
-*/
-static int porterOpen(
- sqlite3_tokenizer *pTokenizer, /* The tokenizer */
- const char *zInput, int nInput, /* String to be tokenized */
- sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
-){
- porter_tokenizer_cursor *c;
-
- c = (porter_tokenizer_cursor *) malloc(sizeof(*c));
- if( c==NULL ) return SQLITE_NOMEM;
-
- c->zInput = zInput;
- if( zInput==0 ){
- c->nInput = 0;
- }else if( nInput<0 ){
- c->nInput = (int)strlen(zInput);
- }else{
- c->nInput = nInput;
- }
- c->iOffset = 0; /* start tokenizing at the beginning */
- c->iToken = 0;
- c->zToken = NULL; /* no space allocated, yet. */
- c->nAllocated = 0;
-
- *ppCursor = &c->base;
- return SQLITE_OK;
-}
-
-/*
-** Close a tokenization cursor previously opened by a call to
-** porterOpen() above.
-*/
-static int porterClose(sqlite3_tokenizer_cursor *pCursor){
- porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
- free(c->zToken);
- free(c);
- return SQLITE_OK;
-}
-/*
-** Vowel or consonant
-*/
-static const char cType[] = {
- 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
- 1, 1, 1, 2, 1
-};
-
-/*
-** isConsonant() and isVowel() determine if their first character in
-** the string they point to is a consonant or a vowel, according
-** to Porter ruls.
-**
-** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'.
-** 'Y' is a consonant unless it follows another consonant,
-** in which case it is a vowel.
-**
-** In these routine, the letters are in reverse order. So the 'y' rule
-** is that 'y' is a consonant unless it is followed by another
-** consonent.
-*/
-static int isVowel(const char*);
-static int isConsonant(const char *z){
- int j;
- char x = *z;
- if( x==0 ) return 0;
- assert( x>='a' && x<='z' );
- j = cType[x-'a'];
- if( j<2 ) return j;
- return z[1]==0 || isVowel(z + 1);
-}
-static int isVowel(const char *z){
- int j;
- char x = *z;
- if( x==0 ) return 0;
- assert( x>='a' && x<='z' );
- j = cType[x-'a'];
- if( j<2 ) return 1-j;
- return isConsonant(z + 1);
-}
-
-/*
-** Let any sequence of one or more vowels be represented by V and let
-** C be sequence of one or more consonants. Then every word can be
-** represented as:
-**
-** [C] (VC){m} [V]
-**
-** In prose: A word is an optional consonant followed by zero or
-** vowel-consonant pairs followed by an optional vowel. "m" is the
-** number of vowel consonant pairs. This routine computes the value
-** of m for the first i bytes of a word.
-**
-** Return true if the m-value for z is 1 or more. In other words,
-** return true if z contains at least one vowel that is followed
-** by a consonant.
-**
-** In this routine z[] is in reverse order. So we are really looking
-** for an instance of of a consonant followed by a vowel.
-*/
-static int m_gt_0(const char *z){
- while( isVowel(z) ){ z++; }
- if( *z==0 ) return 0;
- while( isConsonant(z) ){ z++; }
- return *z!=0;
-}
-
-/* Like mgt0 above except we are looking for a value of m which is
-** exactly 1
-*/
-static int m_eq_1(const char *z){
- while( isVowel(z) ){ z++; }
- if( *z==0 ) return 0;
- while( isConsonant(z) ){ z++; }
- if( *z==0 ) return 0;
- while( isVowel(z) ){ z++; }
- if( *z==0 ) return 1;
- while( isConsonant(z) ){ z++; }
- return *z==0;
-}
-
-/* Like mgt0 above except we are looking for a value of m>1 instead
-** or m>0
-*/
-static int m_gt_1(const char *z){
- while( isVowel(z) ){ z++; }
- if( *z==0 ) return 0;
- while( isConsonant(z) ){ z++; }
- if( *z==0 ) return 0;
- while( isVowel(z) ){ z++; }
- if( *z==0 ) return 0;
- while( isConsonant(z) ){ z++; }
- return *z!=0;
-}
-
-/*
-** Return TRUE if there is a vowel anywhere within z[0..n-1]
-*/
-static int hasVowel(const char *z){
- while( isConsonant(z) ){ z++; }
- return *z!=0;
-}
-
-/*
-** Return TRUE if the word ends in a double consonant.
-**
-** The text is reversed here. So we are really looking at
-** the first two characters of z[].
-*/
-static int doubleConsonant(const char *z){
- return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
-}
-
-/*
-** Return TRUE if the word ends with three letters which
-** are consonant-vowel-consonent and where the final consonant
-** is not 'w', 'x', or 'y'.
-**
-** The word is reversed here. So we are really checking the
-** first three letters and the first one cannot be in [wxy].
-*/
-static int star_oh(const char *z){
- return
- z[0]!=0 && isConsonant(z) &&
- z[0]!='w' && z[0]!='x' && z[0]!='y' &&
- z[1]!=0 && isVowel(z+1) &&
- z[2]!=0 && isConsonant(z+2);
-}
-
-/*
-** If the word ends with zFrom and xCond() is true for the stem
-** of the word that preceeds the zFrom ending, then change the
-** ending to zTo.
-**
-** The input word *pz and zFrom are both in reverse order. zTo
-** is in normal order.
-**
-** Return TRUE if zFrom matches. Return FALSE if zFrom does not
-** match. Not that TRUE is returned even if xCond() fails and
-** no substitution occurs.
-*/
-static int stem(
- char **pz, /* The word being stemmed (Reversed) */
- const char *zFrom, /* If the ending matches this... (Reversed) */
- const char *zTo, /* ... change the ending to this (not reversed) */
- int (*xCond)(const char*) /* Condition that must be true */
-){
- char *z = *pz;
- while( *zFrom && *zFrom==*z ){ z++; zFrom++; }
- if( *zFrom!=0 ) return 0;
- if( xCond && !xCond(z) ) return 1;
- while( *zTo ){
- *(--z) = *(zTo++);
- }
- *pz = z;
- return 1;
-}
-
-/*
-** This is the fallback stemmer used when the porter stemmer is
-** inappropriate. The input word is copied into the output with
-** US-ASCII case folding. If the input word is too long (more
-** than 20 bytes if it contains no digits or more than 6 bytes if
-** it contains digits) then word is truncated to 20 or 6 bytes
-** by taking 10 or 3 bytes from the beginning and end.
-*/
-static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
- int i, mx, j;
- int hasDigit = 0;
- for(i=0; i='A' && c<='Z' ){
- zOut[i] = c - 'A' + 'a';
- }else{
- if( c>='0' && c<='9' ) hasDigit = 1;
- zOut[i] = c;
- }
- }
- mx = hasDigit ? 3 : 10;
- if( nIn>mx*2 ){
- for(j=mx, i=nIn-mx; i=sizeof(zReverse)-7 ){
- /* The word is too big or too small for the porter stemmer.
- ** Fallback to the copy stemmer */
- copy_stemmer(zIn, nIn, zOut, pnOut);
- return;
- }
- for(i=0, j=sizeof(zReverse)-6; i='A' && c<='Z' ){
- zReverse[j] = c + 'a' - 'A';
- }else if( c>='a' && c<='z' ){
- zReverse[j] = c;
- }else{
- /* The use of a character not in [a-zA-Z] means that we fallback
- ** to the copy stemmer */
- copy_stemmer(zIn, nIn, zOut, pnOut);
- return;
- }
- }
- memset(&zReverse[sizeof(zReverse)-5], 0, 5);
- z = &zReverse[j+1];
-
-
- /* Step 1a */
- if( z[0]=='s' ){
- if(
- !stem(&z, "sess", "ss", 0) &&
- !stem(&z, "sei", "i", 0) &&
- !stem(&z, "ss", "ss", 0)
- ){
- z++;
- }
- }
-
- /* Step 1b */
- z2 = z;
- if( stem(&z, "dee", "ee", m_gt_0) ){
- /* Do nothing. The work was all in the test */
- }else if(
- (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
- && z!=z2
- ){
- if( stem(&z, "ta", "ate", 0) ||
- stem(&z, "lb", "ble", 0) ||
- stem(&z, "zi", "ize", 0) ){
- /* Do nothing. The work was all in the test */
- }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){
- z++;
- }else if( m_eq_1(z) && star_oh(z) ){
- *(--z) = 'e';
- }
- }
-
- /* Step 1c */
- if( z[0]=='y' && hasVowel(z+1) ){
- z[0] = 'i';
- }
-
- /* Step 2 */
- switch( z[1] ){
- case 'a':
- stem(&z, "lanoita", "ate", m_gt_0) ||
- stem(&z, "lanoit", "tion", m_gt_0);
- break;
- case 'c':
- stem(&z, "icne", "ence", m_gt_0) ||
- stem(&z, "icna", "ance", m_gt_0);
- break;
- case 'e':
- stem(&z, "rezi", "ize", m_gt_0);
- break;
- case 'g':
- stem(&z, "igol", "log", m_gt_0);
- break;
- case 'l':
- stem(&z, "ilb", "ble", m_gt_0) ||
- stem(&z, "illa", "al", m_gt_0) ||
- stem(&z, "iltne", "ent", m_gt_0) ||
- stem(&z, "ile", "e", m_gt_0) ||
- stem(&z, "ilsuo", "ous", m_gt_0);
- break;
- case 'o':
- stem(&z, "noitazi", "ize", m_gt_0) ||
- stem(&z, "noita", "ate", m_gt_0) ||
- stem(&z, "rota", "ate", m_gt_0);
- break;
- case 's':
- stem(&z, "msila", "al", m_gt_0) ||
- stem(&z, "ssenevi", "ive", m_gt_0) ||
- stem(&z, "ssenluf", "ful", m_gt_0) ||
- stem(&z, "ssensuo", "ous", m_gt_0);
- break;
- case 't':
- stem(&z, "itila", "al", m_gt_0) ||
- stem(&z, "itivi", "ive", m_gt_0) ||
- stem(&z, "itilib", "ble", m_gt_0);
- break;
- }
-
- /* Step 3 */
- switch( z[0] ){
- case 'e':
- stem(&z, "etaci", "ic", m_gt_0) ||
- stem(&z, "evita", "", m_gt_0) ||
- stem(&z, "ezila", "al", m_gt_0);
- break;
- case 'i':
- stem(&z, "itici", "ic", m_gt_0);
- break;
- case 'l':
- stem(&z, "laci", "ic", m_gt_0) ||
- stem(&z, "luf", "", m_gt_0);
- break;
- case 's':
- stem(&z, "ssen", "", m_gt_0);
- break;
- }
-
- /* Step 4 */
- switch( z[1] ){
- case 'a':
- if( z[0]=='l' && m_gt_1(z+2) ){
- z += 2;
- }
- break;
- case 'c':
- if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){
- z += 4;
- }
- break;
- case 'e':
- if( z[0]=='r' && m_gt_1(z+2) ){
- z += 2;
- }
- break;
- case 'i':
- if( z[0]=='c' && m_gt_1(z+2) ){
- z += 2;
- }
- break;
- case 'l':
- if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){
- z += 4;
- }
- break;
- case 'n':
- if( z[0]=='t' ){
- if( z[2]=='a' ){
- if( m_gt_1(z+3) ){
- z += 3;
- }
- }else if( z[2]=='e' ){
- stem(&z, "tneme", "", m_gt_1) ||
- stem(&z, "tnem", "", m_gt_1) ||
- stem(&z, "tne", "", m_gt_1);
- }
- }
- break;
- case 'o':
- if( z[0]=='u' ){
- if( m_gt_1(z+2) ){
- z += 2;
- }
- }else if( z[3]=='s' || z[3]=='t' ){
- stem(&z, "noi", "", m_gt_1);
- }
- break;
- case 's':
- if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){
- z += 3;
- }
- break;
- case 't':
- stem(&z, "eta", "", m_gt_1) ||
- stem(&z, "iti", "", m_gt_1);
- break;
- case 'u':
- if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
- z += 3;
- }
- break;
- case 'v':
- case 'z':
- if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){
- z += 3;
- }
- break;
- }
-
- /* Step 5a */
- if( z[0]=='e' ){
- if( m_gt_1(z+1) ){
- z++;
- }else if( m_eq_1(z+1) && !star_oh(z+1) ){
- z++;
- }
- }
-
- /* Step 5b */
- if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){
- z++;
- }
-
- /* z[] is now the stemmed word in reverse order. Flip it back
- ** around into forward order and return.
- */
- *pnOut = i = strlen(z);
- zOut[i] = 0;
- while( *z ){
- zOut[--i] = *(z++);
- }
-}
-
-/*
-** Characters that can be part of a token. We assume any character
-** whose value is greater than 0x80 (any UTF character) can be
-** part of a token. In other words, delimiters all must have
-** values of 0x7f or lower.
-*/
-static const char isIdChar[] = {
-/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
-};
-#define idChar(C) (((ch=C)&0x80)!=0 || (ch>0x2f && isIdChar[ch-0x30]))
-#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !isIdChar[ch-0x30]))
-
-/*
-** Extract the next token from a tokenization cursor. The cursor must
-** have been opened by a prior call to porterOpen().
-*/
-static int porterNext(
- sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */
- const char **pzToken, /* OUT: *pzToken is the token text */
- int *pnBytes, /* OUT: Number of bytes in token */
- int *piStartOffset, /* OUT: Starting offset of token */
- int *piEndOffset, /* OUT: Ending offset of token */
- int *piPosition /* OUT: Position integer of token */
-){
- porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor;
- const char *z = c->zInput;
-
- while( c->iOffsetnInput ){
- int iStartOffset, ch;
-
- /* Scan past delimiter characters */
- while( c->iOffsetnInput && isDelim(z[c->iOffset]) ){
- c->iOffset++;
- }
-
- /* Count non-delimiter characters. */
- iStartOffset = c->iOffset;
- while( c->iOffsetnInput && !isDelim(z[c->iOffset]) ){
- c->iOffset++;
- }
-
- if( c->iOffset>iStartOffset ){
- int n = c->iOffset-iStartOffset;
- if( n>c->nAllocated ){
- c->nAllocated = n+20;
- c->zToken = realloc(c->zToken, c->nAllocated);
- if( c->zToken==NULL ) return SQLITE_NOMEM;
- }
- porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes);
- *pzToken = c->zToken;
- *piStartOffset = iStartOffset;
- *piEndOffset = c->iOffset;
- *piPosition = c->iToken++;
- return SQLITE_OK;
- }
- }
- return SQLITE_DONE;
-}
-
-/*
-** The set of routines that implement the porter-stemmer tokenizer
-*/
-static const sqlite3_tokenizer_module porterTokenizerModule = {
- 0,
- porterCreate,
- porterDestroy,
- porterOpen,
- porterClose,
- porterNext,
-};
-
-/*
-** Allocate a new porter tokenizer. Return a pointer to the new
-** tokenizer in *ppModule
-*/
-void sqlite3Fts1PorterTokenizerModule(
- sqlite3_tokenizer_module const**ppModule
-){
- *ppModule = &porterTokenizerModule;
-}
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/sqlite/ext/fts1/fts1_tokenizer.h b/sqlite/ext/fts1/fts1_tokenizer.h
deleted file mode 100644
index a48cb745..00000000
--- a/sqlite/ext/fts1/fts1_tokenizer.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-** 2006 July 10
-**
-** The author disclaims copyright to this source code.
-**
-*************************************************************************
-** Defines the interface to tokenizers used by fulltext-search. There
-** are three basic components:
-**
-** sqlite3_tokenizer_module is a singleton defining the tokenizer
-** interface functions. This is essentially the class structure for
-** tokenizers.
-**
-** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
-** including customization information defined at creation time.
-**
-** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
-** tokens from a particular input.
-*/
-#ifndef _FTS1_TOKENIZER_H_
-#define _FTS1_TOKENIZER_H_
-
-/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
-** If tokenizers are to be allowed to call sqlite3_*() functions, then
-** we will need a way to register the API consistently.
-*/
-#include "sqlite3.h"
-
-/*
-** Structures used by the tokenizer interface.
-*/
-typedef struct sqlite3_tokenizer sqlite3_tokenizer;
-typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
-typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
-
-struct sqlite3_tokenizer_module {
- int iVersion; /* currently 0 */
-
- /*
- ** Create and destroy a tokenizer. argc/argv are passed down from
- ** the fulltext virtual table creation to allow customization.
- */
- int (*xCreate)(int argc, const char *const*argv,
- sqlite3_tokenizer **ppTokenizer);
- int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
-
- /*
- ** Tokenize a particular input. Call xOpen() to prepare to
- ** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
- ** xClose() to free any internal state. The pInput passed to
- ** xOpen() must exist until the cursor is closed. The ppToken
- ** result from xNext() is only valid until the next call to xNext()
- ** or until xClose() is called.
- */
- /* TODO(shess) current implementation requires pInput to be
- ** nul-terminated. This should either be fixed, or pInput/nBytes
- ** should be converted to zInput.
- */
- int (*xOpen)(sqlite3_tokenizer *pTokenizer,
- const char *pInput, int nBytes,
- sqlite3_tokenizer_cursor **ppCursor);
- int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
- int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
- const char **ppToken, int *pnBytes,
- int *piStartOffset, int *piEndOffset, int *piPosition);
-};
-
-struct sqlite3_tokenizer {
- const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
- /* Tokenizer implementations will typically add additional fields */
-};
-
-struct sqlite3_tokenizer_cursor {
- sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
- /* Tokenizer implementations will typically add additional fields */
-};
-
-/*
-** Get the module for a tokenizer which generates tokens based on a
-** set of non-token characters. The default is to break tokens at any
-** non-alnum character, though the set of delimiters can also be
-** specified by the first argv argument to xCreate().
-*/
-/* TODO(shess) This doesn't belong here. Need some sort of
-** registration process.
-*/
-void sqlite3Fts1SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-void sqlite3Fts1PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-
-#endif /* _FTS1_TOKENIZER_H_ */
diff --git a/sqlite/ext/fts1/fts1_tokenizer1.c b/sqlite/ext/fts1/fts1_tokenizer1.c
deleted file mode 100644
index f58fba8f..00000000
--- a/sqlite/ext/fts1/fts1_tokenizer1.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
-** The author disclaims copyright to this source code.
-**
-*************************************************************************
-** Implementation of the "simple" full-text-search tokenizer.
-*/
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS1 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS1 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
-
-
-#include
-#include
-#include
-#include
-#include
-
-#include "fts1_tokenizer.h"
-
-typedef struct simple_tokenizer {
- sqlite3_tokenizer base;
- char delim[128]; /* flag ASCII delimiters */
-} simple_tokenizer;
-
-typedef struct simple_tokenizer_cursor {
- sqlite3_tokenizer_cursor base;
- const char *pInput; /* input we are tokenizing */
- int nBytes; /* size of the input */
- int iOffset; /* current position in pInput */
- int iToken; /* index of next token to be returned */
- char *pToken; /* storage for current token */
- int nTokenAllocated; /* space allocated to zToken buffer */
-} simple_tokenizer_cursor;
-
-
-/* Forward declaration */
-static const sqlite3_tokenizer_module simpleTokenizerModule;
-
-static int isDelim(simple_tokenizer *t, unsigned char c){
- return c<0x80 && t->delim[c];
-}
-
-/*
-** Create a new tokenizer instance.
-*/
-static int simpleCreate(
- int argc, const char * const *argv,
- sqlite3_tokenizer **ppTokenizer
-){
- simple_tokenizer *t;
-
- t = (simple_tokenizer *) calloc(sizeof(*t), 1);
- if( t==NULL ) return SQLITE_NOMEM;
-
- /* TODO(shess) Delimiters need to remain the same from run to run,
- ** else we need to reindex. One solution would be a meta-table to
- ** track such information in the database, then we'd only want this
- ** information on the initial create.
- */
- if( argc>1 ){
- int i, n = strlen(argv[1]);
- for(i=0; i=0x80 ){
- free(t);
- return SQLITE_ERROR;
- }
- t->delim[ch] = 1;
- }
- } else {
- /* Mark non-alphanumeric ASCII characters as delimiters */
- int i;
- for(i=1; i<0x80; i++){
- t->delim[i] = !isalnum(i);
- }
- }
-
- *ppTokenizer = &t->base;
- return SQLITE_OK;
-}
-
-/*
-** Destroy a tokenizer
-*/
-static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
- free(pTokenizer);
- return SQLITE_OK;
-}
-
-/*
-** Prepare to begin tokenizing a particular string. The input
-** string to be tokenized is pInput[0..nBytes-1]. A cursor
-** used to incrementally tokenize this string is returned in
-** *ppCursor.
-*/
-static int simpleOpen(
- sqlite3_tokenizer *pTokenizer, /* The tokenizer */
- const char *pInput, int nBytes, /* String to be tokenized */
- sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
-){
- simple_tokenizer_cursor *c;
-
- c = (simple_tokenizer_cursor *) malloc(sizeof(*c));
- if( c==NULL ) return SQLITE_NOMEM;
-
- c->pInput = pInput;
- if( pInput==0 ){
- c->nBytes = 0;
- }else if( nBytes<0 ){
- c->nBytes = (int)strlen(pInput);
- }else{
- c->nBytes = nBytes;
- }
- c->iOffset = 0; /* start tokenizing at the beginning */
- c->iToken = 0;
- c->pToken = NULL; /* no space allocated, yet. */
- c->nTokenAllocated = 0;
-
- *ppCursor = &c->base;
- return SQLITE_OK;
-}
-
-/*
-** Close a tokenization cursor previously opened by a call to
-** simpleOpen() above.
-*/
-static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
- simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
- free(c->pToken);
- free(c);
- return SQLITE_OK;
-}
-
-/*
-** Extract the next token from a tokenization cursor. The cursor must
-** have been opened by a prior call to simpleOpen().
-*/
-static int simpleNext(
- sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
- const char **ppToken, /* OUT: *ppToken is the token text */
- int *pnBytes, /* OUT: Number of bytes in token */
- int *piStartOffset, /* OUT: Starting offset of token */
- int *piEndOffset, /* OUT: Ending offset of token */
- int *piPosition /* OUT: Position integer of token */
-){
- simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
- simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
- unsigned char *p = (unsigned char *)c->pInput;
-
- while( c->iOffsetnBytes ){
- int iStartOffset;
-
- /* Scan past delimiter characters */
- while( c->iOffsetnBytes && isDelim(t, p[c->iOffset]) ){
- c->iOffset++;
- }
-
- /* Count non-delimiter characters. */
- iStartOffset = c->iOffset;
- while( c->iOffsetnBytes && !isDelim(t, p[c->iOffset]) ){
- c->iOffset++;
- }
-
- if( c->iOffset>iStartOffset ){
- int i, n = c->iOffset-iStartOffset;
- if( n>c->nTokenAllocated ){
- c->nTokenAllocated = n+20;
- c->pToken = realloc(c->pToken, c->nTokenAllocated);
- if( c->pToken==NULL ) return SQLITE_NOMEM;
- }
- for(i=0; ipToken[i] = ch<0x80 ? tolower(ch) : ch;
- }
- *ppToken = c->pToken;
- *pnBytes = n;
- *piStartOffset = iStartOffset;
- *piEndOffset = c->iOffset;
- *piPosition = c->iToken++;
-
- return SQLITE_OK;
- }
- }
- return SQLITE_DONE;
-}
-
-/*
-** The set of routines that implement the simple tokenizer
-*/
-static const sqlite3_tokenizer_module simpleTokenizerModule = {
- 0,
- simpleCreate,
- simpleDestroy,
- simpleOpen,
- simpleClose,
- simpleNext,
-};
-
-/*
-** Allocate a new simple tokenizer. Return a pointer to the new
-** tokenizer in *ppModule
-*/
-void sqlite3Fts1SimpleTokenizerModule(
- sqlite3_tokenizer_module const**ppModule
-){
- *ppModule = &simpleTokenizerModule;
-}
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */
diff --git a/sqlite/ext/fts1/fulltext.c b/sqlite/ext/fts1/fulltext.c
deleted file mode 100644
index 313ff303..00000000
--- a/sqlite/ext/fts1/fulltext.c
+++ /dev/null
@@ -1,1511 +0,0 @@
-/* The author disclaims copyright to this source code.
- *
- * This is an SQLite module implementing full-text search.
- */
-
-#include
-#if !defined(__APPLE__)
-#include
-#else
-#include
-#endif
-#include
-#include
-#include
-
-#include "fulltext.h"
-#include "ft_hash.h"
-#include "tokenizer.h"
-#include "sqlite3.h"
-#include "sqlite3ext.h"
-SQLITE_EXTENSION_INIT1
-
-/* utility functions */
-
-/* We encode variable-length integers in little-endian order using seven bits
- * per byte as follows:
-**
-** KEY:
-** A = 0xxxxxxx 7 bits of data and one flag bit
-** B = 1xxxxxxx 7 bits of data and one flag bit
-**
-** 7 bits - A
-** 14 bits - BA
-** 21 bits - BBA
-** and so on.
-*/
-
-/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
-#define VARINT_MAX 10
-
-/* Write a 64-bit variable-length integer to memory starting at p[0].
- * The length of data written will be between 1 and VARINT_MAX bytes.
- * The number of bytes written is returned. */
-static int putVarint(char *p, sqlite_int64 v){
- unsigned char *q = (unsigned char *) p;
- sqlite_uint64 vu = v;
- do{
- *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
- vu >>= 7;
- }while( vu!=0 );
- q[-1] &= 0x7f; /* turn off high bit in final byte */
- assert( q - (unsigned char *)p <= VARINT_MAX );
- return (int) (q - (unsigned char *)p);
-}
-
-/* Read a 64-bit variable-length integer from memory starting at p[0].
- * Return the number of bytes read, or 0 on error.
- * The value is stored in *v. */
-static int getVarint(const char *p, sqlite_int64 *v){
- const unsigned char *q = (const unsigned char *) p;
- sqlite_uint64 x = 0, y = 1;
- while( (*q & 0x80) == 0x80 ){
- x += y * (*q++ & 0x7f);
- y <<= 7;
- if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */
- assert( 0 );
- return 0;
- }
- }
- x += y * (*q++);
- *v = (sqlite_int64) x;
- return (int) (q - (unsigned char *)p);
-}
-
-static int getVarint32(const char *p, int *pi){
- sqlite_int64 i;
- int ret = getVarint(p, &i);
- *pi = (int) i;
- assert( *pi==i );
- return ret;
-}
-
-/*** Document lists ***
- *
- * A document list holds a sorted list of varint-encoded document IDs.
- *
- * A doclist with type DL_POSITIONS_OFFSETS is stored like this:
- *
- * array {
- * varint docid;
- * array {
- * varint position; (delta from previous position plus 1, or 0 for end)
- * varint startOffset; (delta from previous startOffset)
- * varint endOffset; (delta from startOffset)
- * }
- * }
- *
- * Here, array { X } means zero or more occurrences of X, adjacent in memory.
- *
- * A doclist with type DL_POSITIONS is like the above, but holds only docids
- * and positions without offset information.
- *
- * A doclist with type DL_DOCIDS is like the above, but holds only docids
- * without positions or offset information.
- *
- * On disk, every document list has positions and offsets, so we don't bother
- * to serialize a doclist's type.
- *
- * We don't yet delta-encode document IDs; doing so will probably be a
- * modest win.
- *
- * NOTE(shess) I've thought of a slightly (1%) better offset encoding.
- * After the first offset, estimate the next offset by using the
- * current token position and the previous token position and offset,
- * offset to handle some variance. So the estimate would be
- * (iPosition*w->iStartOffset/w->iPosition-64), which is delta-encoded
- * as normal. Offsets more than 64 chars from the estimate are
- * encoded as the delta to the previous start offset + 128. An
- * additional tiny increment can be gained by using the end offset of
- * the previous token to make the estimate a tiny bit more precise.
-*/
-
-typedef enum DocListType {
- DL_DOCIDS, /* docids only */
- DL_POSITIONS, /* docids + positions */
- DL_POSITIONS_OFFSETS /* docids + positions + offsets */
-} DocListType;
-
-typedef struct DocList {
- char *pData;
- int nData;
- DocListType iType;
- int iLastPos; /* the last position written */
- int iLastOffset; /* the last start offset written */
-} DocList;
-
-/* Initialize a new DocList to hold the given data. */
-static void docListInit(DocList *d, DocListType iType,
- const char *pData, int nData){
- d->nData = nData;
- if( nData>0 ){
- d->pData = malloc(nData);
- memcpy(d->pData, pData, nData);
- } else {
- d->pData = NULL;
- }
- d->iType = iType;
- d->iLastPos = 0;
- d->iLastOffset = 0;
-}
-
-/* Create a new dynamically-allocated DocList. */
-static DocList *docListNew(DocListType iType){
- DocList *d = (DocList *) malloc(sizeof(DocList));
- docListInit(d, iType, 0, 0);
- return d;
-}
-
-static void docListDestroy(DocList *d){
- free(d->pData);
-#ifndef NDEBUG
- memset(d, 0x55, sizeof(*d));
-#endif
-}
-
-static void docListDelete(DocList *d){
- docListDestroy(d);
- free(d);
-}
-
-static char *docListEnd(DocList *d){
- return d->pData + d->nData;
-}
-
-/* Append a varint to a DocList's data. */
-static void appendVarint(DocList *d, sqlite_int64 i){
- char c[VARINT_MAX];
- int n = putVarint(c, i);
- d->pData = realloc(d->pData, d->nData + n);
- memcpy(d->pData + d->nData, c, n);
- d->nData += n;
-}
-
-static void docListAddDocid(DocList *d, sqlite_int64 iDocid){
- appendVarint(d, iDocid);
- d->iLastPos = 0;
-}
-
-/* Add a position to the last position list in a doclist. */
-static void docListAddPos(DocList *d, int iPos){
- assert( d->iType>=DL_POSITIONS );
- appendVarint(d, iPos-d->iLastPos+1);
- d->iLastPos = iPos;
-}
-
-static void docListAddPosOffset(DocList *d, int iPos,
- int iStartOffset, int iEndOffset){
- assert( d->iType==DL_POSITIONS_OFFSETS );
- docListAddPos(d, iPos);
- appendVarint(d, iStartOffset-d->iLastOffset);
- d->iLastOffset = iStartOffset;
- appendVarint(d, iEndOffset-iStartOffset);
-}
-
-/* Terminate the last position list in the given doclist. */
-static void docListAddEndPos(DocList *d){
- appendVarint(d, 0);
-}
-
-typedef struct DocListReader {
- DocList *pDoclist;
- char *p;
- int iLastPos; /* the last position read */
-} DocListReader;
-
-static void readerInit(DocListReader *r, DocList *pDoclist){
- r->pDoclist = pDoclist;
- if( pDoclist!=NULL ){
- r->p = pDoclist->pData;
- }
- r->iLastPos = 0;
-}
-
-static int readerAtEnd(DocListReader *pReader){
- return pReader->p >= docListEnd(pReader->pDoclist);
-}
-
-/* Peek at the next docid without advancing the read pointer. */
-static sqlite_int64 peekDocid(DocListReader *pReader){
- sqlite_int64 ret;
- assert( !readerAtEnd(pReader) );
- getVarint(pReader->p, &ret);
- return ret;
-}
-
-/* Read the next docid. */
-static sqlite_int64 readDocid(DocListReader *pReader){
- sqlite_int64 ret;
- assert( !readerAtEnd(pReader) );
- pReader->p += getVarint(pReader->p, &ret);
- pReader->iLastPos = 0;
- return ret;
-}
-
-/* Read the next position from a position list.
- * Returns the position, or -1 at the end of the list. */
-static int readPosition(DocListReader *pReader){
- int i;
- int iType = pReader->pDoclist->iType;
- assert( iType>=DL_POSITIONS );
- assert( !readerAtEnd(pReader) );
-
- pReader->p += getVarint32(pReader->p, &i);
- if( i==0 ){
- pReader->iLastPos = -1;
- return -1;
- }
- pReader->iLastPos += ((int) i)-1;
- if( iType>=DL_POSITIONS_OFFSETS ){
- /* Skip over offsets, ignoring them for now. */
- int iStart, iEnd;
- pReader->p += getVarint32(pReader->p, &iStart);
- pReader->p += getVarint32(pReader->p, &iEnd);
- }
- return pReader->iLastPos;
-}
-
-/* Skip past the end of a position list. */
-static void skipPositionList(DocListReader *pReader){
- while( readPosition(pReader)!=-1 )
- ;
-}
-
-/* Skip over a docid, including its position list if the doclist has
- * positions. */
-static void skipDocument(DocListReader *pReader){
- readDocid(pReader);
- if( pReader->pDoclist->iType >= DL_POSITIONS ){
- skipPositionList(pReader);
- }
-}
-
-static sqlite_int64 firstDocid(DocList *d){
- DocListReader r;
- readerInit(&r, d);
- return readDocid(&r);
-}
-
-/* Doclist multi-tool. Pass pUpdate==NULL to delete the indicated docid;
- * otherwise pUpdate, which must contain only the single docid [iDocid], is
- * inserted (if not present) or updated (if already present). */
-static int docListUpdate(DocList *d, sqlite_int64 iDocid, DocList *pUpdate){
- int modified = 0;
- DocListReader reader;
- char *p;
-
- if( pUpdate!=NULL ){
- assert( d->iType==pUpdate->iType);
- assert( iDocid==firstDocid(pUpdate) );
- }
-
- readerInit(&reader, d);
- while( !readerAtEnd(&reader) && peekDocid(&reader)nData -= (reader.p - p);
- modified = 1;
- }
-
- /* Insert if indicated. */
- if( pUpdate!=NULL ){
- int iDoclist = p-d->pData;
- docListAddEndPos(pUpdate);
-
- d->pData = realloc(d->pData, d->nData+pUpdate->nData);
- p = d->pData + iDoclist;
-
- memmove(p+pUpdate->nData, p, docListEnd(d) - p);
- memcpy(p, pUpdate->pData, pUpdate->nData);
- d->nData += pUpdate->nData;
- modified = 1;
- }
-
- return modified;
-}
-
-/* Split the second half of doclist d into a separate doclist d2. Returns 1
- * if successful, or 0 if d contains a single document and hence can't be
- * split. */
-static int docListSplit(DocList *d, DocList *d2){
- const char *pSplitPoint = d->pData + d->nData / 2;
- DocListReader reader;
-
- readerInit(&reader, d);
- while( reader.piType, reader.p, docListEnd(d) - reader.p);
- d->nData = reader.p - d->pData;
- d->pData = realloc(d->pData, d->nData);
- return 1;
-}
-
-/* A DocListMerge computes the AND of an in-memory DocList [in] and a chunked
- * on-disk doclist, resulting in another in-memory DocList [out]. [in]
- * and [out] may or may not store position information according to the
- * caller's wishes. The on-disk doclist always comes with positions.
- *
- * The caller must read each chunk of the on-disk doclist in succession and
- * pass it to mergeBlock().
- *
- * If [in] has positions, then the merge output contains only documents with
- * matching positions in the two input doclists. If [in] does not have
- * positions, then the merge output contains all documents common to the two
- * input doclists.
- *
- * If [in] is NULL, then the on-disk doclist is copied to [out] directly.
- *
- * A merge is performed using an integer [iOffset] provided by the caller.
- * [iOffset] is subtracted from each position in the on-disk doclist for the
- * purpose of position comparison; this is helpful in implementing phrase
- * searches.
- *
- * A DocListMerge is not yet able to propagate offsets through query
- * processing; we should add that capability soon.
-*/
-typedef struct DocListMerge {
- DocListReader in;
- DocList *pOut;
- int iOffset;
-} DocListMerge;
-
-static void mergeInit(DocListMerge *m,
- DocList *pIn, int iOffset, DocList *pOut){
- readerInit(&m->in, pIn);
- m->pOut = pOut;
- m->iOffset = iOffset;
-
- /* can't handle offsets yet */
- assert( pIn==NULL || pIn->iType <= DL_POSITIONS );
- assert( pOut->iType <= DL_POSITIONS );
-}
-
-/* A helper function for mergeBlock(), below. Merge the position lists
- * pointed to by m->in and pBlockReader.
- * If the merge matches, write [iDocid] to m->pOut; if m->pOut
- * has positions then write all matching positions as well. */
-static void mergePosList(DocListMerge *m, sqlite_int64 iDocid,
- DocListReader *pBlockReader){
- int block_pos = readPosition(pBlockReader);
- int in_pos = readPosition(&m->in);
- int match = 0;
- while( block_pos!=-1 || in_pos!=-1 ){
- if( block_pos-m->iOffset==in_pos ){
- if( !match ){
- docListAddDocid(m->pOut, iDocid);
- match = 1;
- }
- if( m->pOut->iType >= DL_POSITIONS ){
- docListAddPos(m->pOut, in_pos);
- }
- block_pos = readPosition(pBlockReader);
- in_pos = readPosition(&m->in);
- } else if( in_pos==-1 || (block_pos!=-1 && block_pos-m->iOffsetin);
- }
- }
- if( m->pOut->iType >= DL_POSITIONS && match ){
- docListAddEndPos(m->pOut);
- }
-}
-
-/* Merge one block of an on-disk doclist into a DocListMerge. */
-static void mergeBlock(DocListMerge *m, DocList *pBlock){
- DocListReader blockReader;
- assert( pBlock->iType >= DL_POSITIONS );
- readerInit(&blockReader, pBlock);
- while( !readerAtEnd(&blockReader) ){
- sqlite_int64 iDocid = readDocid(&blockReader);
- if( m->in.pDoclist!=NULL ){
- while( 1 ){
- if( readerAtEnd(&m->in) ) return; /* nothing more to merge */
- if( peekDocid(&m->in)>=iDocid ) break;
- skipDocument(&m->in);
- }
- if( peekDocid(&m->in)>iDocid ){ /* [pIn] has no match with iDocid */
- skipPositionList(&blockReader); /* skip this docid in the block */
- continue;
- }
- readDocid(&m->in);
- }
- /* We have a document match. */
- if( m->in.pDoclist==NULL || m->in.pDoclist->iType < DL_POSITIONS ){
- /* We don't need to do a poslist merge. */
- docListAddDocid(m->pOut, iDocid);
- if( m->pOut->iType >= DL_POSITIONS ){
- /* Copy all positions to the output doclist. */
- while( 1 ){
- int pos = readPosition(&blockReader);
- if( pos==-1 ) break;
- docListAddPos(m->pOut, pos);
- }
- docListAddEndPos(m->pOut);
- } else skipPositionList(&blockReader);
- continue;
- }
- mergePosList(m, iDocid, &blockReader);
- }
-}
-
-static char *string_dup_n(const char *s, int n){
- char *str = malloc(n + 1);
- memcpy(str, s, n);
- str[n] = '\0';
- return str;
-}
-
-/* Duplicate a string; the caller must free() the returned string.
- * (We don't use strdup() since it's not part of the standard C library and
- * may not be available everywhere.) */
-static char *string_dup(const char *s){
- return string_dup_n(s, strlen(s));
-}
-
-/* Format a string, replacing each occurrence of the % character with
- * zName. This may be more convenient than sqlite_mprintf()
- * when one string is used repeatedly in a format string.
- * The caller must free() the returned string. */
-static char *string_format(const char *zFormat, const char *zName){
- const char *p;
- size_t len = 0;
- size_t nName = strlen(zName);
- char *result;
- char *r;
-
- /* first compute length needed */
- for(p = zFormat ; *p ; ++p){
- len += (*p=='%' ? nName : 1);
- }
- len += 1; /* for null terminator */
-
- r = result = malloc(len);
- for(p = zFormat; *p; ++p){
- if( *p=='%' ){
- memcpy(r, zName, nName);
- r += nName;
- } else {
- *r++ = *p;
- }
- }
- *r++ = '\0';
- assert( r == result + len );
- return result;
-}
-
-static int sql_exec(sqlite3 *db, const char *zName, const char *zFormat){
- char *zCommand = string_format(zFormat, zName);
- int rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
- free(zCommand);
- return rc;
-}
-
-static int sql_prepare(sqlite3 *db, const char *zName, sqlite3_stmt **ppStmt,
- const char *zFormat){
- char *zCommand = string_format(zFormat, zName);
- int rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
- free(zCommand);
- return rc;
-}
-
-/* end utility functions */
-
-#define QUERY_GENERIC 0
-#define QUERY_FULLTEXT 1
-
-#define CHUNK_MAX 1024
-
-typedef enum fulltext_statement {
- CONTENT_INSERT_STMT,
- CONTENT_SELECT_STMT,
- CONTENT_DELETE_STMT,
-
- TERM_SELECT_STMT,
- TERM_CHUNK_SELECT_STMT,
- TERM_INSERT_STMT,
- TERM_UPDATE_STMT,
- TERM_DELETE_STMT,
-
- MAX_STMT /* Always at end! */
-} fulltext_statement;
-
-/* These must exactly match the enum above. */
-/* TODO(adam): Is there some risk that a statement (in particular,
-** pTermSelectStmt) will be used in two cursors at once, e.g. if a
-** query joins a virtual table to itself? If so perhaps we should
-** move some of these to the cursor object.
-*/
-static const char *fulltext_zStatement[MAX_STMT] = {
- /* CONTENT_INSERT */ "insert into %_content (rowid, content) values (?, ?)",
- /* CONTENT_SELECT */ "select content from %_content where rowid = ?",
- /* CONTENT_DELETE */ "delete from %_content where rowid = ?",
-
- /* TERM_SELECT */
- "select rowid, doclist from %_term where term = ? and first = ?",
- /* TERM_CHUNK_SELECT */
- "select max(first) from %_term where term = ? and first <= ?",
- /* TERM_INSERT */
- "insert into %_term (term, first, doclist) values (?, ?, ?)",
- /* TERM_UPDATE */ "update %_term set doclist = ? where rowid = ?",
- /* TERM_DELETE */ "delete from %_term where rowid = ?",
-};
-
-typedef struct fulltext_vtab {
- sqlite3_vtab base;
- sqlite3 *db;
- const char *zName; /* virtual table name */
- sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
-
- /* Precompiled statements which we keep as long as the table is
- ** open.
- */
- sqlite3_stmt *pFulltextStatements[MAX_STMT];
-} fulltext_vtab;
-
-typedef struct fulltext_cursor {
- sqlite3_vtab_cursor base;
- int iCursorType; /* QUERY_GENERIC or QUERY_FULLTEXT */
-
- sqlite3_stmt *pStmt;
-
- int eof;
-
- /* The following is used only when iCursorType == QUERY_FULLTEXT. */
- DocListReader result;
-} fulltext_cursor;
-
-static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
- return (fulltext_vtab *) c->base.pVtab;
-}
-
-static sqlite3_module fulltextModule; /* forward declaration */
-
-/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
-** If the indicated statement has never been prepared, it is prepared
-** and cached, otherwise the cached version is reset.
-*/
-static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- assert( iStmtpFulltextStatements[iStmt]==NULL ){
- int rc = sql_prepare(v->db, v->zName, &v->pFulltextStatements[iStmt],
- fulltext_zStatement[iStmt]);
- if( rc!=SQLITE_OK ) return rc;
- } else {
- int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- *ppStmt = v->pFulltextStatements[iStmt];
- return SQLITE_OK;
-}
-
-/* Step the indicated statement, handling errors SQLITE_BUSY (by
-** retrying) and SQLITE_SCHEMA (by re-preparing and transferring
-** bindings to the new statement).
-** TODO(adam): We should extend this function so that it can work with
-** statements declared locally, not only globally cached statements.
-*/
-static int sql_step_statement(fulltext_vtab *v, fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- int rc;
- sqlite3_stmt *s = *ppStmt;
- assert( iStmtpFulltextStatements[iStmt] );
-
- while( (rc=sqlite3_step(s))!=SQLITE_DONE && rc!=SQLITE_ROW ){
- sqlite3_stmt *pNewStmt;
-
- if( rc==SQLITE_BUSY ) continue;
- if( rc!=SQLITE_ERROR ) return rc;
-
- rc = sqlite3_reset(s);
- if( rc!=SQLITE_SCHEMA ) return SQLITE_ERROR;
-
- v->pFulltextStatements[iStmt] = NULL; /* Still in s */
- rc = sql_get_statement(v, iStmt, &pNewStmt);
- if( rc!=SQLITE_OK ) goto err;
- *ppStmt = pNewStmt;
-
- rc = sqlite3_transfer_bindings(s, pNewStmt);
- if( rc!=SQLITE_OK ) goto err;
-
- rc = sqlite3_finalize(s);
- if( rc!=SQLITE_OK ) return rc;
- s = pNewStmt;
- }
- return rc;
-
- err:
- sqlite3_finalize(s);
- return rc;
-}
-
-/* Like sql_step_statement(), but convert SQLITE_DONE to SQLITE_OK.
-** Useful for statements like UPDATE, where we expect no results.
-*/
-static int sql_single_step_statement(fulltext_vtab *v,
- fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- int rc = sql_step_statement(v, iStmt, ppStmt);
- return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
-}
-
-/* insert into %_content (rowid, content) values ([rowid], [zContent]) */
-static int content_insert(fulltext_vtab *v, sqlite3_value *rowid,
- const char *zContent, int nContent){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_value(s, 1, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(s, 2, zContent, nContent, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, CONTENT_INSERT_STMT, &s);
-}
-
-/* select content from %_content where rowid = [iRow]
- * The caller must delete the returned string. */
-static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
- char **pzContent){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_step_statement(v, CONTENT_SELECT_STMT, &s);
- if( rc!=SQLITE_ROW ) return rc;
-
- *pzContent = string_dup((const char *)sqlite3_column_text(s, 0));
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ) return SQLITE_OK;
-
- free(*pzContent);
- return rc;
-}
-
-/* delete from %_content where rowid = [iRow ] */
-static int content_delete(fulltext_vtab *v, sqlite_int64 iRow){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, CONTENT_DELETE_STMT, &s);
-}
-
-/* select rowid, doclist from %_term where term = [zTerm] and first = [iFirst]
- * If found, returns SQLITE_OK; the caller must free the returned doclist.
- * If no rows found, returns SQLITE_ERROR. */
-static int term_select(fulltext_vtab *v, const char *zTerm, int nTerm,
- sqlite_int64 iFirst,
- sqlite_int64 *rowid,
- DocList *out){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(s, 1, zTerm, nTerm, SQLITE_TRANSIENT);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, iFirst);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_step_statement(v, TERM_SELECT_STMT, &s);
- if( rc!=SQLITE_ROW ) return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
-
- *rowid = sqlite3_column_int64(s, 0);
- docListInit(out, DL_POSITIONS_OFFSETS,
- sqlite3_column_blob(s, 1), sqlite3_column_bytes(s, 1));
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- return rc==SQLITE_DONE ? SQLITE_OK : rc;
-}
-
-/* select max(first) from %_term where term = [zTerm] and first <= [iFirst]
- * If found, returns SQLITE_ROW and result in *piResult; if the query returns
- * NULL (meaning no row found) returns SQLITE_DONE.
- */
-static int term_chunk_select(fulltext_vtab *v, const char *zTerm, int nTerm,
- sqlite_int64 iFirst, sqlite_int64 *piResult){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_CHUNK_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(s, 1, zTerm, nTerm, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, iFirst);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_step_statement(v, TERM_CHUNK_SELECT_STMT, &s);
- if( rc!=SQLITE_ROW ) return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
-
- switch( sqlite3_column_type(s, 0) ){
- case SQLITE_NULL:
- rc = SQLITE_DONE;
- break;
- case SQLITE_INTEGER:
- *piResult = sqlite3_column_int64(s, 0);
- break;
- default:
- return SQLITE_ERROR;
- }
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- if( sqlite3_step(s) != SQLITE_DONE ) return SQLITE_ERROR;
- return rc;
-}
-
-/* insert into %_term (term, first, doclist)
- values ([zTerm], [iFirst], [doclist]) */
-static int term_insert(fulltext_vtab *v, const char *zTerm, int nTerm,
- sqlite_int64 iFirst, DocList *doclist){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_INSERT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(s, 1, zTerm, nTerm, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, iFirst);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_blob(s, 3, doclist->pData, doclist->nData, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, TERM_INSERT_STMT, &s);
-}
-
-/* update %_term set doclist = [doclist] where rowid = [rowid] */
-static int term_update(fulltext_vtab *v, sqlite_int64 rowid,
- DocList *doclist){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_UPDATE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_blob(s, 1, doclist->pData, doclist->nData,
- SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, TERM_UPDATE_STMT, &s);
-}
-
-static int term_delete(fulltext_vtab *v, sqlite_int64 rowid){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, TERM_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step_statement(v, TERM_DELETE_STMT, &s);
-}
-
-static void fulltext_vtab_destroy(fulltext_vtab *v){
- int iStmt;
-
- for( iStmt=0; iStmtpFulltextStatements[iStmt]!=NULL ){
- sqlite3_finalize(v->pFulltextStatements[iStmt]);
- v->pFulltextStatements[iStmt] = NULL;
- }
- }
-
- if( v->pTokenizer!=NULL ){
- v->pTokenizer->pModule->xDestroy(v->pTokenizer);
- v->pTokenizer = NULL;
- }
-
- free((void *) v->zName);
- free(v);
-}
-
-/* Current interface:
-** argv[0] - module name
-** argv[1] - database name
-** argv[2] - table name
-** argv[3] - tokenizer name (optional, a sensible default is provided)
-** argv[4..] - passed to tokenizer (optional based on tokenizer)
-**/
-static int fulltextConnect(
- sqlite3 *db,
- void *pAux,
- int argc,
- const char * const *argv,
- sqlite3_vtab **ppVTab,
- char **pzErr
-){
- int rc;
- fulltext_vtab *v;
- sqlite3_tokenizer_module *m = NULL;
-
- assert( argc>=3 );
- v = (fulltext_vtab *) malloc(sizeof(fulltext_vtab));
- /* sqlite will initialize v->base */
- v->db = db;
- v->zName = string_dup(argv[2]);
- v->pTokenizer = NULL;
-
- if( argc==3 ){
- get_simple_tokenizer_module(&m);
- } else {
- /* TODO(shess) For now, add new tokenizers as else if clauses. */
- if( !strcmp(argv[3], "simple") ){
- get_simple_tokenizer_module(&m);
- } else {
- assert( "unrecognized tokenizer"==NULL );
- }
- }
-
- /* TODO(shess) Since tokenization impacts the index, the parameters
- ** to the tokenizer need to be identical when a persistent virtual
- ** table is re-created. One solution would be a meta-table to track
- ** such information in the database. Then we could verify that the
- ** information is identical on subsequent creates.
- */
- /* TODO(shess) Why isn't argv already (const char **)? */
- rc = m->xCreate(argc-3, (const char **) (argv+3), &v->pTokenizer);
- if( rc!=SQLITE_OK ) return rc;
- v->pTokenizer->pModule = m;
-
- /* TODO: verify the existence of backing tables foo_content, foo_term */
-
- rc = sqlite3_declare_vtab(db, "create table x(content text)");
- if( rc!=SQLITE_OK ) return rc;
-
- memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
-
- *ppVTab = &v->base;
- return SQLITE_OK;
-}
-
-static int fulltextCreate(
- sqlite3 *db,
- void *pAux,
- int argc,
- const char * const *argv,
- sqlite3_vtab **ppVTab,
- char **pzErr
-){
- int rc;
- assert( argc>=3 );
-
- /* The %_content table holds the text of each full-text item, with
- ** the rowid used as the docid.
- **
- ** The %_term table maps each term to a document list blob
- ** containing elements sorted by ascending docid, each element
- ** encoded as:
- **
- ** docid varint-encoded
- ** token count varint-encoded
- ** "count" token elements (poslist):
- ** position varint-encoded as delta from previous position
- ** start offset varint-encoded as delta from previous start offset
- ** end offset varint-encoded as delta from start offset
- **
- ** Additionally, doclist blobs can be chunked into multiple rows,
- ** using "first" to order the blobs. "first" is simply the first
- ** docid in the blob.
- */
- /*
- ** NOTE(shess) That last sentence is incorrect in the face of
- ** deletion, which can leave a doclist that doesn't contain the
- ** first from that row. I _believe_ this does not matter to the
- ** operation of the system, but it might be reasonable to update
- ** appropriately in case this assumption becomes more important.
- */
- rc = sql_exec(db, argv[2],
- "create table %_content(content text);"
- "create table %_term(term text, first integer, doclist blob);"
- "create index %_index on %_term(term, first)");
- if( rc!=SQLITE_OK ) return rc;
-
- return fulltextConnect(db, pAux, argc, argv, ppVTab, pzErr);
-}
-
-/* Decide how to handle an SQL query.
- * At the moment, MATCH queries can include implicit boolean ANDs; we
- * haven't implemented phrase searches or OR yet. */
-static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
- int i;
-
- for(i=0; inConstraint; ++i){
- const struct sqlite3_index_constraint *pConstraint;
- pConstraint = &pInfo->aConstraint[i];
- if( pConstraint->iColumn==0 &&
- pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH &&
- pConstraint->usable ){ /* a full-text search */
- pInfo->aConstraintUsage[i].argvIndex = 1;
- pInfo->aConstraintUsage[i].omit = 1;
- pInfo->idxNum = QUERY_FULLTEXT;
- pInfo->estimatedCost = 1.0; /* an arbitrary value for now */
- return SQLITE_OK;
- }
- }
- pInfo->idxNum = QUERY_GENERIC;
- return SQLITE_OK;
-}
-
-static int fulltextDisconnect(sqlite3_vtab *pVTab){
- fulltext_vtab_destroy((fulltext_vtab *)pVTab);
- return SQLITE_OK;
-}
-
-static int fulltextDestroy(sqlite3_vtab *pVTab){
- fulltext_vtab *v = (fulltext_vtab *)pVTab;
-
- int rc = sql_exec(v->db, v->zName,
- "drop table %_content; drop table %_term");
- if( rc!=SQLITE_OK ) return rc;
-
- fulltext_vtab_destroy((fulltext_vtab *)pVTab);
- return SQLITE_OK;
-}
-
-static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
- fulltext_cursor *c;
-
- c = (fulltext_cursor *) calloc(sizeof(fulltext_cursor), 1);
- /* sqlite will initialize c->base */
- *ppCursor = &c->base;
-
- return SQLITE_OK;
-}
-
-static int fulltextClose(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- sqlite3_finalize(c->pStmt);
- if( c->result.pDoclist!=NULL ){
- docListDelete(c->result.pDoclist);
- }
- free(c);
- return SQLITE_OK;
-}
-
-static int fulltextNext(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- sqlite_int64 iDocid;
- int rc;
-
- switch( c->iCursorType ){
- case QUERY_GENERIC:
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
- rc = sqlite3_step(c->pStmt);
- switch( rc ){
- case SQLITE_ROW:
- c->eof = 0;
- return SQLITE_OK;
- case SQLITE_DONE:
- c->eof = 1;
- return SQLITE_OK;
- default:
- c->eof = 1;
- return rc;
- }
- case QUERY_FULLTEXT:
- rc = sqlite3_reset(c->pStmt);
- if( rc!=SQLITE_OK ) return rc;
-
- if( readerAtEnd(&c->result)){
- c->eof = 1;
- return SQLITE_OK;
- }
- iDocid = readDocid(&c->result);
- rc = sqlite3_bind_int64(c->pStmt, 1, iDocid);
- if( rc!=SQLITE_OK ) return rc;
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
- rc = sqlite3_step(c->pStmt);
- if( rc==SQLITE_ROW ){ /* the case we expect */
- c->eof = 0;
- return SQLITE_OK;
- }
- /* an error occurred; abort */
- return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
- default:
- assert( 0 );
- return SQLITE_ERROR; /* not reached */
- }
-}
-
-static int term_select_doclist(fulltext_vtab *v, const char *pTerm, int nTerm,
- sqlite3_stmt **ppStmt){
- int rc;
- if( *ppStmt ){
- rc = sqlite3_reset(*ppStmt);
- } else {
- rc = sql_prepare(v->db, v->zName, ppStmt,
- "select doclist from %_term where term = ? order by first");
- }
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_text(*ppStmt, 1, pTerm, nTerm, SQLITE_TRANSIENT);
- if( rc!=SQLITE_OK ) return rc;
-
- return sqlite3_step(*ppStmt); /* TODO(adamd): handle schema error */
-}
-
-/* Read the posting list for [zTerm]; AND it with the doclist [in] to
- * produce the doclist [out], using the given offset [iOffset] for phrase
- * matching.
- * (*pSelect) is used to hold an SQLite statement used inside this function;
- * the caller should initialize *pSelect to NULL before the first call.
- */
-static int query_merge(fulltext_vtab *v, sqlite3_stmt **pSelect,
- const char *zTerm,
- DocList *pIn, int iOffset, DocList *out){
- int rc;
- DocListMerge merge;
-
- if( pIn!=NULL && !pIn->nData ){
- /* If [pIn] is already empty, there's no point in reading the
- * posting list to AND it in; return immediately. */
- return SQLITE_OK;
- }
-
- rc = term_select_doclist(v, zTerm, -1, pSelect);
- if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc;
-
- mergeInit(&merge, pIn, iOffset, out);
- while( rc==SQLITE_ROW ){
- DocList block;
- docListInit(&block, DL_POSITIONS_OFFSETS,
- sqlite3_column_blob(*pSelect, 0),
- sqlite3_column_bytes(*pSelect, 0));
- mergeBlock(&merge, &block);
- docListDestroy(&block);
-
- rc = sqlite3_step(*pSelect);
- if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
- return rc;
- }
- }
-
- return SQLITE_OK;
-}
-
-typedef struct QueryTerm {
- int is_phrase; /* true if this term begins a new phrase */
- const char *zTerm;
-} QueryTerm;
-
-/* A parsed query.
- *
- * As an example, parsing the query ["four score" years "new nation"] will
- * yield a Query with 5 terms:
- * "four", is_phrase = 1
- * "score", is_phrase = 0
- * "years", is_phrase = 1
- * "new", is_phrase = 1
- * "nation", is_phrase = 0
- */
-typedef struct Query {
- int nTerms;
- QueryTerm *pTerm;
-} Query;
-
-static void query_add(Query *q, int is_phrase, const char *zTerm){
- QueryTerm *t;
- ++q->nTerms;
- q->pTerm = realloc(q->pTerm, q->nTerms * sizeof(q->pTerm[0]));
- t = &q->pTerm[q->nTerms - 1];
- t->is_phrase = is_phrase;
- t->zTerm = zTerm;
-}
-
-static void query_free(Query *q){
- int i;
- for(i = 0; i < q->nTerms; ++i){
- free((void *) q->pTerm[i].zTerm);
- }
- free(q->pTerm);
-}
-
-static int tokenize_segment(sqlite3_tokenizer *pTokenizer,
- const char *zQuery, int in_phrase,
- Query *pQuery){
- sqlite3_tokenizer_module *pModule = pTokenizer->pModule;
- sqlite3_tokenizer_cursor *pCursor;
- int is_first = 1;
-
- int rc = pModule->xOpen(pTokenizer, zQuery, -1, &pCursor);
- if( rc!=SQLITE_OK ) return rc;
- pCursor->pTokenizer = pTokenizer;
-
- while( 1 ){
- const char *zToken;
- int nToken, iStartOffset, iEndOffset, dummy_pos;
-
- rc = pModule->xNext(pCursor,
- &zToken, &nToken,
- &iStartOffset, &iEndOffset,
- &dummy_pos);
- if( rc!=SQLITE_OK ) break;
- query_add(pQuery, !in_phrase || is_first, string_dup_n(zToken, nToken));
- is_first = 0;
- }
-
- return pModule->xClose(pCursor);
-}
-
-/* Parse a query string, yielding a Query object. */
-static int parse_query(fulltext_vtab *v, const char *zQuery, Query *pQuery){
- char *zQuery1 = string_dup(zQuery);
- int in_phrase = 0;
- char *s = zQuery1;
- pQuery->nTerms = 0;
- pQuery->pTerm = NULL;
-
- while( *s ){
- char *t = s;
- while( *t ){
- if( *t=='"' ){
- *t++ = '\0';
- break;
- }
- ++t;
- }
- if( *s ){
- tokenize_segment(v->pTokenizer, s, in_phrase, pQuery);
- }
- s = t;
- in_phrase = !in_phrase;
- }
-
- free(zQuery1);
- return SQLITE_OK;
-}
-
-/* Perform a full-text query; return a list of documents in [pResult]. */
-static int fulltext_query(fulltext_vtab *v, const char *zQuery,
- DocList **pResult){
- Query q;
- int phrase_start = -1;
- int i;
- sqlite3_stmt *pSelect = NULL;
- DocList *d = NULL;
-
- int rc = parse_query(v, zQuery, &q);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Merge terms. */
- for(i = 0 ; i < q.nTerms ; ++i){
- /* In each merge step, we need to generate positions whenever we're
- * processing a phrase which hasn't ended yet. */
- int need_positions = iiCursorType = idxNum;
- switch( idxNum ){
- case QUERY_GENERIC:
- zStatement = "select rowid, content from %_content";
- break;
-
- case QUERY_FULLTEXT: /* full-text search */
- {
- const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
- DocList *pResult;
- assert( argc==1 );
- rc = fulltext_query(v, zQuery, &pResult);
- if( rc!=SQLITE_OK ) return rc;
- readerInit(&c->result, pResult);
- zStatement = "select rowid, content from %_content where rowid = ?";
- break;
- }
-
- default:
- assert( 0 );
- }
-
- rc = sql_prepare(v->db, v->zName, &c->pStmt, zStatement);
- if( rc!=SQLITE_OK ) return rc;
-
- return fulltextNext(pCursor);
-}
-
-static int fulltextEof(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- return c->eof;
-}
-
-static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
- sqlite3_context *pContext, int idxCol){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- const char *s;
-
- assert( idxCol==0 );
- s = (const char *) sqlite3_column_text(c->pStmt, 1);
- sqlite3_result_text(pContext, s, -1, SQLITE_TRANSIENT);
-
- return SQLITE_OK;
-}
-
-static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
-
- *pRowid = sqlite3_column_int64(c->pStmt, 0);
- return SQLITE_OK;
-}
-
-/* Build a hash table containing all terms in zText. */
-static int build_terms(Hash *terms, sqlite3_tokenizer *pTokenizer,
- const char *zText, sqlite_int64 iDocid){
- sqlite3_tokenizer_cursor *pCursor;
- const char *pToken;
- int nTokenBytes;
- int iStartOffset, iEndOffset, iPosition;
-
- int rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor);
- if( rc!=SQLITE_OK ) return rc;
-
- pCursor->pTokenizer = pTokenizer;
- HashInit(terms, HASH_STRING, 1);
- while( SQLITE_OK==pTokenizer->pModule->xNext(pCursor,
- &pToken, &nTokenBytes,
- &iStartOffset, &iEndOffset,
- &iPosition) ){
- DocList *p;
-
- /* Positions can't be negative; we use -1 as a terminator internally. */
- if( iPosition<0 ) {
- rc = SQLITE_ERROR;
- goto err;
- }
-
- p = HashFind(terms, pToken, nTokenBytes);
- if( p==NULL ){
- p = docListNew(DL_POSITIONS_OFFSETS);
- docListAddDocid(p, iDocid);
- HashInsert(terms, pToken, nTokenBytes, p);
- }
- docListAddPosOffset(p, iPosition, iStartOffset, iEndOffset);
- }
-
-err:
- /* TODO(shess) Check return? Should this be able to cause errors at
- ** this point? Actually, same question about sqlite3_finalize(),
- ** though one could argue that failure there means that the data is
- ** not durable. *ponder*
- */
- pTokenizer->pModule->xClose(pCursor);
- return rc;
-}
-/* Update the %_terms table to map the term [zTerm] to the given rowid. */
-static int index_insert_term(fulltext_vtab *v, const char *zTerm, int nTerm,
- sqlite_int64 iDocid, DocList *p){
- sqlite_int64 iFirst;
- sqlite_int64 iIndexRow;
- DocList doclist;
-
- int rc = term_chunk_select(v, zTerm, nTerm, iDocid, &iFirst);
- if( rc==SQLITE_DONE ){
- docListInit(&doclist, DL_POSITIONS_OFFSETS, 0, 0);
- if( docListUpdate(&doclist, iDocid, p) ){
- rc = term_insert(v, zTerm, nTerm, iDocid, &doclist);
- docListDestroy(&doclist);
- return rc;
- }
- return SQLITE_OK;
- }
- if( rc!=SQLITE_ROW ) return SQLITE_ERROR;
-
- /* This word is in the index; add this document ID to its blob. */
-
- rc = term_select(v, zTerm, nTerm, iFirst, &iIndexRow, &doclist);
- if( rc!=SQLITE_OK ) return rc;
-
- if( docListUpdate(&doclist, iDocid, p) ){
- /* If the blob is too big, split it in half. */
- if( doclist.nData>CHUNK_MAX ){
- DocList half;
- if( docListSplit(&doclist, &half) ){
- rc = term_insert(v, zTerm, nTerm, firstDocid(&half), &half);
- docListDestroy(&half);
- if( rc!=SQLITE_OK ) goto err;
- }
- }
- rc = term_update(v, iIndexRow, &doclist);
- }
-
-err:
- docListDestroy(&doclist);
- return rc;
-}
-
-/* Insert a row into the full-text index; set *piRowid to be the ID of the
- * new row. */
-static int index_insert(fulltext_vtab *v,
- sqlite3_value *pRequestRowid, const char *zText,
- sqlite_int64 *piRowid){
- Hash terms; /* maps term string -> PosList */
- HashElem *e;
-
- int rc = content_insert(v, pRequestRowid, zText, -1);
- if( rc!=SQLITE_OK ) return rc;
- *piRowid = sqlite3_last_insert_rowid(v->db);
-
- if( !zText ) return SQLITE_OK; /* nothing to index */
-
- rc = build_terms(&terms, v->pTokenizer, zText, *piRowid);
- if( rc!=SQLITE_OK ) return rc;
-
- for(e=HashFirst(&terms); e; e=HashNext(e)){
- DocList *p = HashData(e);
- rc = index_insert_term(v, HashKey(e), HashKeysize(e), *piRowid, p);
- if( rc!=SQLITE_OK ) break;
- }
-
- for(e=HashFirst(&terms); e; e=HashNext(e)){
- DocList *p = HashData(e);
- docListDelete(p);
- }
- HashClear(&terms);
- return rc;
-}
-
-static int index_delete_term(fulltext_vtab *v, const char *zTerm, int nTerm,
- sqlite_int64 iDocid){
- sqlite_int64 iFirst;
- sqlite_int64 iIndexRow;
- DocList doclist;
-
- int rc = term_chunk_select(v, zTerm, nTerm, iDocid, &iFirst);
- if( rc!=SQLITE_ROW ) return SQLITE_ERROR;
-
- rc = term_select(v, zTerm, nTerm, iFirst, &iIndexRow, &doclist);
- if( rc!=SQLITE_OK ) return rc;
-
- if( docListUpdate(&doclist, iDocid, NULL) ){
- if( doclist.nData>0 ){
- rc = term_update(v, iIndexRow, &doclist);
- } else { /* empty posting list */
- rc = term_delete(v, iIndexRow);
- }
- }
- docListDestroy(&doclist);
- return rc;
-}
-
-/* Delete a row from the full-text index. */
-static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){
- char *zText;
- Hash terms;
- HashElem *e;
-
- int rc = content_select(v, iRow, &zText);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = build_terms(&terms, v->pTokenizer, zText, iRow);
- free(zText);
- if( rc!=SQLITE_OK ) return rc;
-
- for(e=HashFirst(&terms); e; e=HashNext(e)){
- rc = index_delete_term(v, HashKey(e), HashKeysize(e), iRow);
- if( rc!=SQLITE_OK ) break;
- }
- for(e=HashFirst(&terms); e; e=HashNext(e)){
- DocList *p = HashData(e);
- docListDelete(p);
- }
- HashClear(&terms);
-
- return content_delete(v, iRow);
-}
-
-static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
- sqlite_int64 *pRowid){
- fulltext_vtab *v = (fulltext_vtab *) pVtab;
-
- if( nArg<2 ){
- return index_delete(v, sqlite3_value_int64(ppArg[0]));
- }
-
- if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
- return SQLITE_ERROR; /* an update; not yet supported */
- }
-
- assert( nArg==3 ); /* ppArg[1] = rowid, ppArg[2] = content */
- return index_insert(v, ppArg[1],
- (const char *)sqlite3_value_text(ppArg[2]), pRowid);
-}
-
-static sqlite3_module fulltextModule = {
- 0,
- fulltextCreate,
- fulltextConnect,
- fulltextBestIndex,
- fulltextDisconnect,
- fulltextDestroy,
- fulltextOpen,
- fulltextClose,
- fulltextFilter,
- fulltextNext,
- fulltextEof,
- fulltextColumn,
- fulltextRowid,
- fulltextUpdate
-};
-
-int fulltext_init(sqlite3 *db){
- return sqlite3_create_module(db, "fulltext", &fulltextModule, 0);
-}
-
-#if !SQLITE_CORE
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-int sqlite3_fulltext_init(sqlite3 *db, char **pzErrMsg,
- const sqlite3_api_routines *pApi){
- SQLITE_EXTENSION_INIT2(pApi)
- return fulltext_init(db);
-}
-#endif
diff --git a/sqlite/ext/fts1/fulltext.h b/sqlite/ext/fts1/fulltext.h
deleted file mode 100644
index 477dcab2..00000000
--- a/sqlite/ext/fts1/fulltext.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "sqlite3.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-int fulltext_init(sqlite3 *db);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif /* __cplusplus */
diff --git a/sqlite/ext/fts1/simple_tokenizer.c b/sqlite/ext/fts1/simple_tokenizer.c
deleted file mode 100644
index 0ddc7055..00000000
--- a/sqlite/ext/fts1/simple_tokenizer.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
-** The author disclaims copyright to this source code.
-**
-*************************************************************************
-** Implementation of the "simple" full-text-search tokenizer.
-*/
-
-#include
-#if !defined(__APPLE__)
-#include
-#else
-#include
-#endif
-#include
-#include
-#include
-
-#include "tokenizer.h"
-
-/* Duplicate a string; the caller must free() the returned string.
- * (We don't use strdup() since it's not part of the standard C library and
- * may not be available everywhere.) */
-/* TODO(shess) Copied from fulltext.c, consider util.c for such
-** things. */
-static char *string_dup(const char *s){
- char *str = malloc(strlen(s) + 1);
- strcpy(str, s);
- return str;
-}
-
-typedef struct simple_tokenizer {
- sqlite3_tokenizer base;
- const char *zDelim; /* token delimiters */
-} simple_tokenizer;
-
-typedef struct simple_tokenizer_cursor {
- sqlite3_tokenizer_cursor base;
- const char *pInput; /* input we are tokenizing */
- int nBytes; /* size of the input */
- const char *pCurrent; /* current position in pInput */
- int iToken; /* index of next token to be returned */
- char *zToken; /* storage for current token */
- int nTokenBytes; /* actual size of current token */
- int nTokenAllocated; /* space allocated to zToken buffer */
-} simple_tokenizer_cursor;
-
-static sqlite3_tokenizer_module simpleTokenizerModule;/* forward declaration */
-
-static int simpleCreate(
- int argc, const char **argv,
- sqlite3_tokenizer **ppTokenizer
-){
- simple_tokenizer *t;
-
- t = (simple_tokenizer *) malloc(sizeof(simple_tokenizer));
- /* TODO(shess) Delimiters need to remain the same from run to run,
- ** else we need to reindex. One solution would be a meta-table to
- ** track such information in the database, then we'd only want this
- ** information on the initial create.
- */
- if( argc>1 ){
- t->zDelim = string_dup(argv[1]);
- } else {
- /* Build a string excluding alphanumeric ASCII characters */
- char zDelim[0x80]; /* nul-terminated, so nul not a member */
- int i, j;
- for(i=1, j=0; i<0x80; i++){
- if( !isalnum(i) ){
- zDelim[j++] = i;
- }
- }
- zDelim[j++] = '\0';
- assert( j<=sizeof(zDelim) );
- t->zDelim = string_dup(zDelim);
- }
-
- *ppTokenizer = &t->base;
- return SQLITE_OK;
-}
-
-static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
- simple_tokenizer *t = (simple_tokenizer *) pTokenizer;
-
- free((void *) t->zDelim);
- free(t);
-
- return SQLITE_OK;
-}
-
-static int simpleOpen(
- sqlite3_tokenizer *pTokenizer,
- const char *pInput, int nBytes,
- sqlite3_tokenizer_cursor **ppCursor
-){
- simple_tokenizer_cursor *c;
-
- c = (simple_tokenizer_cursor *) malloc(sizeof(simple_tokenizer_cursor));
- c->pInput = pInput;
- c->nBytes = nBytes<0 ? (int) strlen(pInput) : nBytes;
- c->pCurrent = c->pInput; /* start tokenizing at the beginning */
- c->iToken = 0;
- c->zToken = NULL; /* no space allocated, yet. */
- c->nTokenBytes = 0;
- c->nTokenAllocated = 0;
-
- *ppCursor = &c->base;
- return SQLITE_OK;
-}
-
-static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
- simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
-
- if( NULL!=c->zToken ){
- free(c->zToken);
- }
- free(c);
-
- return SQLITE_OK;
-}
-
-static int simpleNext(
- sqlite3_tokenizer_cursor *pCursor,
- const char **ppToken, int *pnBytes,
- int *piStartOffset, int *piEndOffset, int *piPosition
-){
- simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
- simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
- int ii;
-
- while( c->pCurrent-c->pInputnBytes ){
- int n = (int) strcspn(c->pCurrent, t->zDelim);
- if( n>0 ){
- if( n+1>c->nTokenAllocated ){
- c->zToken = realloc(c->zToken, n+1);
- }
- for(ii=0; iipCurrent[ii];
- c->zToken[ii] = (unsigned char)ch<0x80 ? tolower((unsigned char)ch):ch;
- }
- c->zToken[n] = '\0';
- *ppToken = c->zToken;
- *pnBytes = n;
- *piStartOffset = (int) (c->pCurrent-c->pInput);
- *piEndOffset = *piStartOffset+n;
- *piPosition = c->iToken++;
- c->pCurrent += n + 1;
-
- return SQLITE_OK;
- }
- c->pCurrent += n + 1;
- /* TODO(shess) could strspn() to skip delimiters en masse. Needs
- ** to happen in two places, though, which is annoying.
- */
- }
- return SQLITE_DONE;
-}
-
-static sqlite3_tokenizer_module simpleTokenizerModule = {
- 0,
- simpleCreate,
- simpleDestroy,
- simpleOpen,
- simpleClose,
- simpleNext,
-};
-
-void get_simple_tokenizer_module(
- sqlite3_tokenizer_module **ppModule
-){
- *ppModule = &simpleTokenizerModule;
-}
diff --git a/sqlite/ext/fts1/tokenizer.h b/sqlite/ext/fts1/tokenizer.h
deleted file mode 100644
index 1d7bd1f6..00000000
--- a/sqlite/ext/fts1/tokenizer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-** 2006 July 10
-**
-** The author disclaims copyright to this source code.
-**
-*************************************************************************
-** Defines the interface to tokenizers used by fulltext-search. There
-** are three basic components:
-**
-** sqlite3_tokenizer_module is a singleton defining the tokenizer
-** interface functions. This is essentially the class structure for
-** tokenizers.
-**
-** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
-** including customization information defined at creation time.
-**
-** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
-** tokens from a particular input.
-*/
-#ifndef _TOKENIZER_H_
-#define _TOKENIZER_H_
-
-/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
-** If tokenizers are to be allowed to call sqlite3_*() functions, then
-** we will need a way to register the API consistently.
-*/
-#include "sqlite3.h"
-
-/*
-** Structures used by the tokenizer interface.
-*/
-typedef struct sqlite3_tokenizer sqlite3_tokenizer;
-typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
-typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
-
-struct sqlite3_tokenizer_module {
- int iVersion; /* currently 0 */
-
- /*
- ** Create and destroy a tokenizer. argc/argv are passed down from
- ** the fulltext virtual table creation to allow customization.
- */
- int (*xCreate)(int argc, const char **argv,
- sqlite3_tokenizer **ppTokenizer);
- int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
-
- /*
- ** Tokenize a particular input. Call xOpen() to prepare to
- ** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
- ** xClose() to free any internal state. The pInput passed to
- ** xOpen() must exist until the cursor is closed. The ppToken
- ** result from xNext() is only valid until the next call to xNext()
- ** or until xClose() is called.
- */
- /* TODO(shess) current implementation requires pInput to be
- ** nul-terminated. This should either be fixed, or pInput/nBytes
- ** should be converted to zInput.
- */
- int (*xOpen)(sqlite3_tokenizer *pTokenizer,
- const char *pInput, int nBytes,
- sqlite3_tokenizer_cursor **ppCursor);
- int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
- int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
- const char **ppToken, int *pnBytes,
- int *piStartOffset, int *piEndOffset, int *piPosition);
-};
-
-struct sqlite3_tokenizer {
- sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
- /* Tokenizer implementations will typically add additional fields */
-};
-
-struct sqlite3_tokenizer_cursor {
- sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
- /* Tokenizer implementations will typically add additional fields */
-};
-
-/*
-** Get the module for a tokenizer which generates tokens based on a
-** set of non-token characters. The default is to break tokens at any
-** non-alnum character, though the set of delimiters can also be
-** specified by the first argv argument to xCreate().
-*/
-/* TODO(shess) This doesn't belong here. Need some sort of
-** registration process.
-*/
-void get_simple_tokenizer_module(sqlite3_tokenizer_module **ppModule);
-
-#endif /* _TOKENIZER_H_ */
diff --git a/sqlite/ext/fts2/README.tokenizers b/sqlite/ext/fts2/README.tokenizers
deleted file mode 100644
index 98d2021b..00000000
--- a/sqlite/ext/fts2/README.tokenizers
+++ /dev/null
@@ -1,133 +0,0 @@
-
-1. FTS2 Tokenizers
-
- When creating a new full-text table, FTS2 allows the user to select
- the text tokenizer implementation to be used when indexing text
- by specifying a "tokenizer" clause as part of the CREATE VIRTUAL TABLE
- statement:
-
- CREATE VIRTUAL TABLE USING fts2(
- [, tokenizer []]
- );
-
- The built-in tokenizers (valid values to pass as ) are
- "simple" and "porter".
-
- should consist of zero or more white-space separated
- arguments to pass to the selected tokenizer implementation. The
- interpretation of the arguments, if any, depends on the individual
- tokenizer.
-
-2. Custom Tokenizers
-
- FTS2 allows users to provide custom tokenizer implementations. The
- interface used to create a new tokenizer is defined and described in
- the fts2_tokenizer.h source file.
-
- Registering a new FTS2 tokenizer is similar to registering a new
- virtual table module with SQLite. The user passes a pointer to a
- structure containing pointers to various callback functions that
- make up the implementation of the new tokenizer type. For tokenizers,
- the structure (defined in fts2_tokenizer.h) is called
- "sqlite3_tokenizer_module".
-
- FTS2 does not expose a C-function that users call to register new
- tokenizer types with a database handle. Instead, the pointer must
- be encoded as an SQL blob value and passed to FTS2 through the SQL
- engine by evaluating a special scalar function, "fts2_tokenizer()".
- The fts2_tokenizer() function may be called with one or two arguments,
- as follows:
-
- SELECT fts2_tokenizer();
- SELECT fts2_tokenizer(, );
-
- Where is a string identifying the tokenizer and
- is a pointer to an sqlite3_tokenizer_module
- structure encoded as an SQL blob. If the second argument is present,
- it is registered as tokenizer and a copy of it
- returned. If only one argument is passed, a pointer to the tokenizer
- implementation currently registered as is returned,
- encoded as a blob. Or, if no such tokenizer exists, an SQL exception
- (error) is raised.
-
- SECURITY: If the fts2 extension is used in an environment where potentially
- malicious users may execute arbitrary SQL (i.e. gears), they should be
- prevented from invoking the fts2_tokenizer() function, possibly using the
- authorisation callback.
-
- See "Sample code" below for an example of calling the fts2_tokenizer()
- function from C code.
-
-3. ICU Library Tokenizers
-
- If this extension is compiled with the SQLITE_ENABLE_ICU pre-processor
- symbol defined, then there exists a built-in tokenizer named "icu"
- implemented using the ICU library. The first argument passed to the
- xCreate() method (see fts2_tokenizer.h) of this tokenizer may be
- an ICU locale identifier. For example "tr_TR" for Turkish as used
- in Turkey, or "en_AU" for English as used in Australia. For example:
-
- "CREATE VIRTUAL TABLE thai_text USING fts2(text, tokenizer icu th_TH)"
-
- The ICU tokenizer implementation is very simple. It splits the input
- text according to the ICU rules for finding word boundaries and discards
- any tokens that consist entirely of white-space. This may be suitable
- for some applications in some locales, but not all. If more complex
- processing is required, for example to implement stemming or
- discard punctuation, this can be done by creating a tokenizer
- implementation that uses the ICU tokenizer as part of its implementation.
-
- When using the ICU tokenizer this way, it is safe to overwrite the
- contents of the strings returned by the xNext() method (see
- fts2_tokenizer.h).
-
-4. Sample code.
-
- The following two code samples illustrate the way C code should invoke
- the fts2_tokenizer() scalar function:
-
- int registerTokenizer(
- sqlite3 *db,
- char *zName,
- const sqlite3_tokenizer_module *p
- ){
- int rc;
- sqlite3_stmt *pStmt;
- const char zSql[] = "SELECT fts2_tokenizer(?, ?)";
-
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
-
- sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
- sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
- sqlite3_step(pStmt);
-
- return sqlite3_finalize(pStmt);
- }
-
- int queryTokenizer(
- sqlite3 *db,
- char *zName,
- const sqlite3_tokenizer_module **pp
- ){
- int rc;
- sqlite3_stmt *pStmt;
- const char zSql[] = "SELECT fts2_tokenizer(?)";
-
- *pp = 0;
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
-
- sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
- if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
- memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
- }
- }
-
- return sqlite3_finalize(pStmt);
- }
diff --git a/sqlite/ext/fts2/README.txt b/sqlite/ext/fts2/README.txt
deleted file mode 100644
index 517a2a04..00000000
--- a/sqlite/ext/fts2/README.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This folder contains source code to the second full-text search
-extension for SQLite. While the API is the same, this version uses a
-substantially different storage schema from fts1, so tables will need
-to be rebuilt.
diff --git a/sqlite/ext/fts2/fts2.c b/sqlite/ext/fts2/fts2.c
deleted file mode 100644
index 0405fb7b..00000000
--- a/sqlite/ext/fts2/fts2.c
+++ /dev/null
@@ -1,6860 +0,0 @@
-/* fts2 has a design flaw which can lead to database corruption (see
-** below). It is recommended not to use it any longer, instead use
-** fts3 (or higher). If you believe that your use of fts2 is safe,
-** add -DSQLITE_ENABLE_BROKEN_FTS2=1 to your CFLAGS.
-*/
-#if (!defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)) \
- && !defined(SQLITE_ENABLE_BROKEN_FTS2)
-#error fts2 has a design flaw and has been deprecated.
-#endif
-/* The flaw is that fts2 uses the content table's unaliased rowid as
-** the unique docid. fts2 embeds the rowid in the index it builds,
-** and expects the rowid to not change. The SQLite VACUUM operation
-** will renumber such rowids, thereby breaking fts2. If you are using
-** fts2 in a system which has disabled VACUUM, then you can continue
-** to use it safely. Note that PRAGMA auto_vacuum does NOT disable
-** VACUUM, though systems using auto_vacuum are unlikely to invoke
-** VACUUM.
-**
-** Unlike fts1, which is safe across VACUUM if you never delete
-** documents, fts2 has a second exposure to this flaw, in the segments
-** table. So fts2 should be considered unsafe across VACUUM in all
-** cases.
-*/
-
-/*
-** 2006 Oct 10
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This is an SQLite module implementing full-text search.
-*/
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS2 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS2 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
-*/
-
-/* TODO(shess) Consider exporting this comment to an HTML file or the
-** wiki.
-*/
-/* The full-text index is stored in a series of b+tree (-like)
-** structures called segments which map terms to doclists. The
-** structures are like b+trees in layout, but are constructed from the
-** bottom up in optimal fashion and are not updatable. Since trees
-** are built from the bottom up, things will be described from the
-** bottom up.
-**
-**
-**** Varints ****
-** The basic unit of encoding is a variable-length integer called a
-** varint. We encode variable-length integers in little-endian order
-** using seven bits * per byte as follows:
-**
-** KEY:
-** A = 0xxxxxxx 7 bits of data and one flag bit
-** B = 1xxxxxxx 7 bits of data and one flag bit
-**
-** 7 bits - A
-** 14 bits - BA
-** 21 bits - BBA
-** and so on.
-**
-** This is identical to how sqlite encodes varints (see util.c).
-**
-**
-**** Document lists ****
-** A doclist (document list) holds a docid-sorted list of hits for a
-** given term. Doclists hold docids, and can optionally associate
-** token positions and offsets with docids.
-**
-** A DL_POSITIONS_OFFSETS doclist is stored like this:
-**
-** array {
-** varint docid;
-** array { (position list for column 0)
-** varint position; (delta from previous position plus POS_BASE)
-** varint startOffset; (delta from previous startOffset)
-** varint endOffset; (delta from startOffset)
-** }
-** array {
-** varint POS_COLUMN; (marks start of position list for new column)
-** varint column; (index of new column)
-** array {
-** varint position; (delta from previous position plus POS_BASE)
-** varint startOffset;(delta from previous startOffset)
-** varint endOffset; (delta from startOffset)
-** }
-** }
-** varint POS_END; (marks end of positions for this document.
-** }
-**
-** Here, array { X } means zero or more occurrences of X, adjacent in
-** memory. A "position" is an index of a token in the token stream
-** generated by the tokenizer, while an "offset" is a byte offset,
-** both based at 0. Note that POS_END and POS_COLUMN occur in the
-** same logical place as the position element, and act as sentinals
-** ending a position list array.
-**
-** A DL_POSITIONS doclist omits the startOffset and endOffset
-** information. A DL_DOCIDS doclist omits both the position and
-** offset information, becoming an array of varint-encoded docids.
-**
-** On-disk data is stored as type DL_DEFAULT, so we don't serialize
-** the type. Due to how deletion is implemented in the segmentation
-** system, on-disk doclists MUST store at least positions.
-**
-**
-**** Segment leaf nodes ****
-** Segment leaf nodes store terms and doclists, ordered by term. Leaf
-** nodes are written using LeafWriter, and read using LeafReader (to
-** iterate through a single leaf node's data) and LeavesReader (to
-** iterate through a segment's entire leaf layer). Leaf nodes have
-** the format:
-**
-** varint iHeight; (height from leaf level, always 0)
-** varint nTerm; (length of first term)
-** char pTerm[nTerm]; (content of first term)
-** varint nDoclist; (length of term's associated doclist)
-** char pDoclist[nDoclist]; (content of doclist)
-** array {
-** (further terms are delta-encoded)
-** varint nPrefix; (length of prefix shared with previous term)
-** varint nSuffix; (length of unshared suffix)
-** char pTermSuffix[nSuffix];(unshared suffix of next term)
-** varint nDoclist; (length of term's associated doclist)
-** char pDoclist[nDoclist]; (content of doclist)
-** }
-**
-** Here, array { X } means zero or more occurrences of X, adjacent in
-** memory.
-**
-** Leaf nodes are broken into blocks which are stored contiguously in
-** the %_segments table in sorted order. This means that when the end
-** of a node is reached, the next term is in the node with the next
-** greater node id.
-**
-** New data is spilled to a new leaf node when the current node
-** exceeds LEAF_MAX bytes (default 2048). New data which itself is
-** larger than STANDALONE_MIN (default 1024) is placed in a standalone
-** node (a leaf node with a single term and doclist). The goal of
-** these settings is to pack together groups of small doclists while
-** making it efficient to directly access large doclists. The
-** assumption is that large doclists represent terms which are more
-** likely to be query targets.
-**
-** TODO(shess) It may be useful for blocking decisions to be more
-** dynamic. For instance, it may make more sense to have a 2.5k leaf
-** node rather than splitting into 2k and .5k nodes. My intuition is
-** that this might extend through 2x or 4x the pagesize.
-**
-**
-**** Segment interior nodes ****
-** Segment interior nodes store blockids for subtree nodes and terms
-** to describe what data is stored by the each subtree. Interior
-** nodes are written using InteriorWriter, and read using
-** InteriorReader. InteriorWriters are created as needed when
-** SegmentWriter creates new leaf nodes, or when an interior node
-** itself grows too big and must be split. The format of interior
-** nodes:
-**
-** varint iHeight; (height from leaf level, always >0)
-** varint iBlockid; (block id of node's leftmost subtree)
-** optional {
-** varint nTerm; (length of first term)
-** char pTerm[nTerm]; (content of first term)
-** array {
-** (further terms are delta-encoded)
-** varint nPrefix; (length of shared prefix with previous term)
-** varint nSuffix; (length of unshared suffix)
-** char pTermSuffix[nSuffix]; (unshared suffix of next term)
-** }
-** }
-**
-** Here, optional { X } means an optional element, while array { X }
-** means zero or more occurrences of X, adjacent in memory.
-**
-** An interior node encodes n terms separating n+1 subtrees. The
-** subtree blocks are contiguous, so only the first subtree's blockid
-** is encoded. The subtree at iBlockid will contain all terms less
-** than the first term encoded (or all terms if no term is encoded).
-** Otherwise, for terms greater than or equal to pTerm[i] but less
-** than pTerm[i+1], the subtree for that term will be rooted at
-** iBlockid+i. Interior nodes only store enough term data to
-** distinguish adjacent children (if the rightmost term of the left
-** child is "something", and the leftmost term of the right child is
-** "wicked", only "w" is stored).
-**
-** New data is spilled to a new interior node at the same height when
-** the current node exceeds INTERIOR_MAX bytes (default 2048).
-** INTERIOR_MIN_TERMS (default 7) keeps large terms from monopolizing
-** interior nodes and making the tree too skinny. The interior nodes
-** at a given height are naturally tracked by interior nodes at
-** height+1, and so on.
-**
-**
-**** Segment directory ****
-** The segment directory in table %_segdir stores meta-information for
-** merging and deleting segments, and also the root node of the
-** segment's tree.
-**
-** The root node is the top node of the segment's tree after encoding
-** the entire segment, restricted to ROOT_MAX bytes (default 1024).
-** This could be either a leaf node or an interior node. If the top
-** node requires more than ROOT_MAX bytes, it is flushed to %_segments
-** and a new root interior node is generated (which should always fit
-** within ROOT_MAX because it only needs space for 2 varints, the
-** height and the blockid of the previous root).
-**
-** The meta-information in the segment directory is:
-** level - segment level (see below)
-** idx - index within level
-** - (level,idx uniquely identify a segment)
-** start_block - first leaf node
-** leaves_end_block - last leaf node
-** end_block - last block (including interior nodes)
-** root - contents of root node
-**
-** If the root node is a leaf node, then start_block,
-** leaves_end_block, and end_block are all 0.
-**
-**
-**** Segment merging ****
-** To amortize update costs, segments are groups into levels and
-** merged in matches. Each increase in level represents exponentially
-** more documents.
-**
-** New documents (actually, document updates) are tokenized and
-** written individually (using LeafWriter) to a level 0 segment, with
-** incrementing idx. When idx reaches MERGE_COUNT (default 16), all
-** level 0 segments are merged into a single level 1 segment. Level 1
-** is populated like level 0, and eventually MERGE_COUNT level 1
-** segments are merged to a single level 2 segment (representing
-** MERGE_COUNT^2 updates), and so on.
-**
-** A segment merge traverses all segments at a given level in
-** parallel, performing a straightforward sorted merge. Since segment
-** leaf nodes are written in to the %_segments table in order, this
-** merge traverses the underlying sqlite disk structures efficiently.
-** After the merge, all segment blocks from the merged level are
-** deleted.
-**
-** MERGE_COUNT controls how often we merge segments. 16 seems to be
-** somewhat of a sweet spot for insertion performance. 32 and 64 show
-** very similar performance numbers to 16 on insertion, though they're
-** a tiny bit slower (perhaps due to more overhead in merge-time
-** sorting). 8 is about 20% slower than 16, 4 about 50% slower than
-** 16, 2 about 66% slower than 16.
-**
-** At query time, high MERGE_COUNT increases the number of segments
-** which need to be scanned and merged. For instance, with 100k docs
-** inserted:
-**
-** MERGE_COUNT segments
-** 16 25
-** 8 12
-** 4 10
-** 2 6
-**
-** This appears to have only a moderate impact on queries for very
-** frequent terms (which are somewhat dominated by segment merge
-** costs), and infrequent and non-existent terms still seem to be fast
-** even with many segments.
-**
-** TODO(shess) That said, it would be nice to have a better query-side
-** argument for MERGE_COUNT of 16. Also, it is possible/likely that
-** optimizations to things like doclist merging will swing the sweet
-** spot around.
-**
-**
-**
-**** Handling of deletions and updates ****
-** Since we're using a segmented structure, with no docid-oriented
-** index into the term index, we clearly cannot simply update the term
-** index when a document is deleted or updated. For deletions, we
-** write an empty doclist (varint(docid) varint(POS_END)), for updates
-** we simply write the new doclist. Segment merges overwrite older
-** data for a particular docid with newer data, so deletes or updates
-** will eventually overtake the earlier data and knock it out. The
-** query logic likewise merges doclists so that newer data knocks out
-** older data.
-**
-** TODO(shess) Provide a VACUUM type operation to clear out all
-** deletions and duplications. This would basically be a forced merge
-** into a single segment.
-*/
-
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
-
-#if defined(SQLITE_ENABLE_FTS2) && !defined(SQLITE_CORE)
-# define SQLITE_CORE 1
-#endif
-
-#include
-#include
-#include
-#include
-#include "fts2.h"
-#include "fts2_hash.h"
-#include "fts2_tokenizer.h"
-#include "sqlite3.h"
-#include "sqlite3ext.h"
-SQLITE_EXTENSION_INIT1
-
-
-/* TODO(shess) MAN, this thing needs some refactoring. At minimum, it
-** would be nice to order the file better, perhaps something along the
-** lines of:
-**
-** - utility functions
-** - table setup functions
-** - table update functions
-** - table query functions
-**
-** Put the query functions last because they're likely to reference
-** typedefs or functions from the table update section.
-*/
-
-#if 0
-# define TRACE(A) printf A; fflush(stdout)
-#else
-# define TRACE(A)
-#endif
-
-/* It is not safe to call isspace(), tolower(), or isalnum() on
-** hi-bit-set characters. This is the same solution used in the
-** tokenizer.
-*/
-/* TODO(shess) The snippet-generation code should be using the
-** tokenizer-generated tokens rather than doing its own local
-** tokenization.
-*/
-/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
-static int safe_isspace(char c){
- return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
-}
-static int safe_tolower(char c){
- return (c>='A' && c<='Z') ? (c - 'A' + 'a') : c;
-}
-static int safe_isalnum(char c){
- return (c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z');
-}
-
-typedef enum DocListType {
- DL_DOCIDS, /* docids only */
- DL_POSITIONS, /* docids + positions */
- DL_POSITIONS_OFFSETS /* docids + positions + offsets */
-} DocListType;
-
-/*
-** By default, only positions and not offsets are stored in the doclists.
-** To change this so that offsets are stored too, compile with
-**
-** -DDL_DEFAULT=DL_POSITIONS_OFFSETS
-**
-** If DL_DEFAULT is set to DL_DOCIDS, your table can only be inserted
-** into (no deletes or updates).
-*/
-#ifndef DL_DEFAULT
-# define DL_DEFAULT DL_POSITIONS
-#endif
-
-enum {
- POS_END = 0, /* end of this position list */
- POS_COLUMN, /* followed by new column number */
- POS_BASE
-};
-
-/* MERGE_COUNT controls how often we merge segments (see comment at
-** top of file).
-*/
-#define MERGE_COUNT 16
-
-/* utility functions */
-
-/* CLEAR() and SCRAMBLE() abstract memset() on a pointer to a single
-** record to prevent errors of the form:
-**
-** my_function(SomeType *b){
-** memset(b, '\0', sizeof(b)); // sizeof(b)!=sizeof(*b)
-** }
-*/
-/* TODO(shess) Obvious candidates for a header file. */
-#define CLEAR(b) memset(b, '\0', sizeof(*(b)))
-
-#ifndef NDEBUG
-# define SCRAMBLE(b) memset(b, 0x55, sizeof(*(b)))
-#else
-# define SCRAMBLE(b)
-#endif
-
-/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */
-#define VARINT_MAX 10
-
-/* Write a 64-bit variable-length integer to memory starting at p[0].
- * The length of data written will be between 1 and VARINT_MAX bytes.
- * The number of bytes written is returned. */
-static int putVarint(char *p, sqlite_int64 v){
- unsigned char *q = (unsigned char *) p;
- sqlite_uint64 vu = v;
- do{
- *q++ = (unsigned char) ((vu & 0x7f) | 0x80);
- vu >>= 7;
- }while( vu!=0 );
- q[-1] &= 0x7f; /* turn off high bit in final byte */
- assert( q - (unsigned char *)p <= VARINT_MAX );
- return (int) (q - (unsigned char *)p);
-}
-
-/* Read a 64-bit variable-length integer from memory starting at p[0].
- * Return the number of bytes read, or 0 on error.
- * The value is stored in *v. */
-static int getVarint(const char *p, sqlite_int64 *v){
- const unsigned char *q = (const unsigned char *) p;
- sqlite_uint64 x = 0, y = 1;
- while( (*q & 0x80) == 0x80 ){
- x += y * (*q++ & 0x7f);
- y <<= 7;
- if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */
- assert( 0 );
- return 0;
- }
- }
- x += y * (*q++);
- *v = (sqlite_int64) x;
- return (int) (q - (unsigned char *)p);
-}
-
-static int getVarint32(const char *p, int *pi){
- sqlite_int64 i;
- int ret = getVarint(p, &i);
- *pi = (int) i;
- assert( *pi==i );
- return ret;
-}
-
-/*******************************************************************/
-/* DataBuffer is used to collect data into a buffer in piecemeal
-** fashion. It implements the usual distinction between amount of
-** data currently stored (nData) and buffer capacity (nCapacity).
-**
-** dataBufferInit - create a buffer with given initial capacity.
-** dataBufferReset - forget buffer's data, retaining capacity.
-** dataBufferDestroy - free buffer's data.
-** dataBufferSwap - swap contents of two buffers.
-** dataBufferExpand - expand capacity without adding data.
-** dataBufferAppend - append data.
-** dataBufferAppend2 - append two pieces of data at once.
-** dataBufferReplace - replace buffer's data.
-*/
-typedef struct DataBuffer {
- char *pData; /* Pointer to malloc'ed buffer. */
- int nCapacity; /* Size of pData buffer. */
- int nData; /* End of data loaded into pData. */
-} DataBuffer;
-
-static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){
- assert( nCapacity>=0 );
- pBuffer->nData = 0;
- pBuffer->nCapacity = nCapacity;
- pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity);
-}
-static void dataBufferReset(DataBuffer *pBuffer){
- pBuffer->nData = 0;
-}
-static void dataBufferDestroy(DataBuffer *pBuffer){
- if( pBuffer->pData!=NULL ) sqlite3_free(pBuffer->pData);
- SCRAMBLE(pBuffer);
-}
-static void dataBufferSwap(DataBuffer *pBuffer1, DataBuffer *pBuffer2){
- DataBuffer tmp = *pBuffer1;
- *pBuffer1 = *pBuffer2;
- *pBuffer2 = tmp;
-}
-static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){
- assert( nAddCapacity>0 );
- /* TODO(shess) Consider expanding more aggressively. Note that the
- ** underlying malloc implementation may take care of such things for
- ** us already.
- */
- if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){
- pBuffer->nCapacity = pBuffer->nData+nAddCapacity;
- pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity);
- }
-}
-static void dataBufferAppend(DataBuffer *pBuffer,
- const char *pSource, int nSource){
- assert( nSource>0 && pSource!=NULL );
- dataBufferExpand(pBuffer, nSource);
- memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource);
- pBuffer->nData += nSource;
-}
-static void dataBufferAppend2(DataBuffer *pBuffer,
- const char *pSource1, int nSource1,
- const char *pSource2, int nSource2){
- assert( nSource1>0 && pSource1!=NULL );
- assert( nSource2>0 && pSource2!=NULL );
- dataBufferExpand(pBuffer, nSource1+nSource2);
- memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1);
- memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2);
- pBuffer->nData += nSource1+nSource2;
-}
-static void dataBufferReplace(DataBuffer *pBuffer,
- const char *pSource, int nSource){
- dataBufferReset(pBuffer);
- dataBufferAppend(pBuffer, pSource, nSource);
-}
-
-/* StringBuffer is a null-terminated version of DataBuffer. */
-typedef struct StringBuffer {
- DataBuffer b; /* Includes null terminator. */
-} StringBuffer;
-
-static void initStringBuffer(StringBuffer *sb){
- dataBufferInit(&sb->b, 100);
- dataBufferReplace(&sb->b, "", 1);
-}
-static int stringBufferLength(StringBuffer *sb){
- return sb->b.nData-1;
-}
-static char *stringBufferData(StringBuffer *sb){
- return sb->b.pData;
-}
-static void stringBufferDestroy(StringBuffer *sb){
- dataBufferDestroy(&sb->b);
-}
-
-static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
- assert( sb->b.nData>0 );
- if( nFrom>0 ){
- sb->b.nData--;
- dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1);
- }
-}
-static void append(StringBuffer *sb, const char *zFrom){
- nappend(sb, zFrom, strlen(zFrom));
-}
-
-/* Append a list of strings separated by commas. */
-static void appendList(StringBuffer *sb, int nString, char **azString){
- int i;
- for(i=0; i0 ) append(sb, ", ");
- append(sb, azString[i]);
- }
-}
-
-static int endsInWhiteSpace(StringBuffer *p){
- return stringBufferLength(p)>0 &&
- safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]);
-}
-
-/* If the StringBuffer ends in something other than white space, add a
-** single space character to the end.
-*/
-static void appendWhiteSpace(StringBuffer *p){
- if( stringBufferLength(p)==0 ) return;
- if( !endsInWhiteSpace(p) ) append(p, " ");
-}
-
-/* Remove white space from the end of the StringBuffer */
-static void trimWhiteSpace(StringBuffer *p){
- while( endsInWhiteSpace(p) ){
- p->b.pData[--p->b.nData-1] = '\0';
- }
-}
-
-/*******************************************************************/
-/* DLReader is used to read document elements from a doclist. The
-** current docid is cached, so dlrDocid() is fast. DLReader does not
-** own the doclist buffer.
-**
-** dlrAtEnd - true if there's no more data to read.
-** dlrDocid - docid of current document.
-** dlrDocData - doclist data for current document (including docid).
-** dlrDocDataBytes - length of same.
-** dlrAllDataBytes - length of all remaining data.
-** dlrPosData - position data for current document.
-** dlrPosDataLen - length of pos data for current document (incl POS_END).
-** dlrStep - step to current document.
-** dlrInit - initial for doclist of given type against given data.
-** dlrDestroy - clean up.
-**
-** Expected usage is something like:
-**
-** DLReader reader;
-** dlrInit(&reader, pData, nData);
-** while( !dlrAtEnd(&reader) ){
-** // calls to dlrDocid() and kin.
-** dlrStep(&reader);
-** }
-** dlrDestroy(&reader);
-*/
-typedef struct DLReader {
- DocListType iType;
- const char *pData;
- int nData;
-
- sqlite_int64 iDocid;
- int nElement;
-} DLReader;
-
-static int dlrAtEnd(DLReader *pReader){
- assert( pReader->nData>=0 );
- return pReader->nData==0;
-}
-static sqlite_int64 dlrDocid(DLReader *pReader){
- assert( !dlrAtEnd(pReader) );
- return pReader->iDocid;
-}
-static const char *dlrDocData(DLReader *pReader){
- assert( !dlrAtEnd(pReader) );
- return pReader->pData;
-}
-static int dlrDocDataBytes(DLReader *pReader){
- assert( !dlrAtEnd(pReader) );
- return pReader->nElement;
-}
-static int dlrAllDataBytes(DLReader *pReader){
- assert( !dlrAtEnd(pReader) );
- return pReader->nData;
-}
-/* TODO(shess) Consider adding a field to track iDocid varint length
-** to make these two functions faster. This might matter (a tiny bit)
-** for queries.
-*/
-static const char *dlrPosData(DLReader *pReader){
- sqlite_int64 iDummy;
- int n = getVarint(pReader->pData, &iDummy);
- assert( !dlrAtEnd(pReader) );
- return pReader->pData+n;
-}
-static int dlrPosDataLen(DLReader *pReader){
- sqlite_int64 iDummy;
- int n = getVarint(pReader->pData, &iDummy);
- assert( !dlrAtEnd(pReader) );
- return pReader->nElement-n;
-}
-static void dlrStep(DLReader *pReader){
- assert( !dlrAtEnd(pReader) );
-
- /* Skip past current doclist element. */
- assert( pReader->nElement<=pReader->nData );
- pReader->pData += pReader->nElement;
- pReader->nData -= pReader->nElement;
-
- /* If there is more data, read the next doclist element. */
- if( pReader->nData!=0 ){
- sqlite_int64 iDocidDelta;
- int iDummy, n = getVarint(pReader->pData, &iDocidDelta);
- pReader->iDocid += iDocidDelta;
- if( pReader->iType>=DL_POSITIONS ){
- assert( nnData );
- while( 1 ){
- n += getVarint32(pReader->pData+n, &iDummy);
- assert( n<=pReader->nData );
- if( iDummy==POS_END ) break;
- if( iDummy==POS_COLUMN ){
- n += getVarint32(pReader->pData+n, &iDummy);
- assert( nnData );
- }else if( pReader->iType==DL_POSITIONS_OFFSETS ){
- n += getVarint32(pReader->pData+n, &iDummy);
- n += getVarint32(pReader->pData+n, &iDummy);
- assert( nnData );
- }
- }
- }
- pReader->nElement = n;
- assert( pReader->nElement<=pReader->nData );
- }
-}
-static void dlrInit(DLReader *pReader, DocListType iType,
- const char *pData, int nData){
- assert( pData!=NULL && nData!=0 );
- pReader->iType = iType;
- pReader->pData = pData;
- pReader->nData = nData;
- pReader->nElement = 0;
- pReader->iDocid = 0;
-
- /* Load the first element's data. There must be a first element. */
- dlrStep(pReader);
-}
-static void dlrDestroy(DLReader *pReader){
- SCRAMBLE(pReader);
-}
-
-#ifndef NDEBUG
-/* Verify that the doclist can be validly decoded. Also returns the
-** last docid found because it is convenient in other assertions for
-** DLWriter.
-*/
-static void docListValidate(DocListType iType, const char *pData, int nData,
- sqlite_int64 *pLastDocid){
- sqlite_int64 iPrevDocid = 0;
- assert( nData>0 );
- assert( pData!=0 );
- assert( pData+nData>pData );
- while( nData!=0 ){
- sqlite_int64 iDocidDelta;
- int n = getVarint(pData, &iDocidDelta);
- iPrevDocid += iDocidDelta;
- if( iType>DL_DOCIDS ){
- int iDummy;
- while( 1 ){
- n += getVarint32(pData+n, &iDummy);
- if( iDummy==POS_END ) break;
- if( iDummy==POS_COLUMN ){
- n += getVarint32(pData+n, &iDummy);
- }else if( iType>DL_POSITIONS ){
- n += getVarint32(pData+n, &iDummy);
- n += getVarint32(pData+n, &iDummy);
- }
- assert( n<=nData );
- }
- }
- assert( n<=nData );
- pData += n;
- nData -= n;
- }
- if( pLastDocid ) *pLastDocid = iPrevDocid;
-}
-#define ASSERT_VALID_DOCLIST(i, p, n, o) docListValidate(i, p, n, o)
-#else
-#define ASSERT_VALID_DOCLIST(i, p, n, o) assert( 1 )
-#endif
-
-/*******************************************************************/
-/* DLWriter is used to write doclist data to a DataBuffer. DLWriter
-** always appends to the buffer and does not own it.
-**
-** dlwInit - initialize to write a given type doclistto a buffer.
-** dlwDestroy - clear the writer's memory. Does not free buffer.
-** dlwAppend - append raw doclist data to buffer.
-** dlwCopy - copy next doclist from reader to writer.
-** dlwAdd - construct doclist element and append to buffer.
-** Only apply dlwAdd() to DL_DOCIDS doclists (else use PLWriter).
-*/
-typedef struct DLWriter {
- DocListType iType;
- DataBuffer *b;
- sqlite_int64 iPrevDocid;
-#ifndef NDEBUG
- int has_iPrevDocid;
-#endif
-} DLWriter;
-
-static void dlwInit(DLWriter *pWriter, DocListType iType, DataBuffer *b){
- pWriter->b = b;
- pWriter->iType = iType;
- pWriter->iPrevDocid = 0;
-#ifndef NDEBUG
- pWriter->has_iPrevDocid = 0;
-#endif
-}
-static void dlwDestroy(DLWriter *pWriter){
- SCRAMBLE(pWriter);
-}
-/* iFirstDocid is the first docid in the doclist in pData. It is
-** needed because pData may point within a larger doclist, in which
-** case the first item would be delta-encoded.
-**
-** iLastDocid is the final docid in the doclist in pData. It is
-** needed to create the new iPrevDocid for future delta-encoding. The
-** code could decode the passed doclist to recreate iLastDocid, but
-** the only current user (docListMerge) already has decoded this
-** information.
-*/
-/* TODO(shess) This has become just a helper for docListMerge.
-** Consider a refactor to make this cleaner.
-*/
-static void dlwAppend(DLWriter *pWriter,
- const char *pData, int nData,
- sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){
- sqlite_int64 iDocid = 0;
- char c[VARINT_MAX];
- int nFirstOld, nFirstNew; /* Old and new varint len of first docid. */
-#ifndef NDEBUG
- sqlite_int64 iLastDocidDelta;
-#endif
-
- /* Recode the initial docid as delta from iPrevDocid. */
- nFirstOld = getVarint(pData, &iDocid);
- assert( nFirstOldiType==DL_DOCIDS) );
- nFirstNew = putVarint(c, iFirstDocid-pWriter->iPrevDocid);
-
- /* Verify that the incoming doclist is valid AND that it ends with
- ** the expected docid. This is essential because we'll trust this
- ** docid in future delta-encoding.
- */
- ASSERT_VALID_DOCLIST(pWriter->iType, pData, nData, &iLastDocidDelta);
- assert( iLastDocid==iFirstDocid-iDocid+iLastDocidDelta );
-
- /* Append recoded initial docid and everything else. Rest of docids
- ** should have been delta-encoded from previous initial docid.
- */
- if( nFirstOldb, c, nFirstNew,
- pData+nFirstOld, nData-nFirstOld);
- }else{
- dataBufferAppend(pWriter->b, c, nFirstNew);
- }
- pWriter->iPrevDocid = iLastDocid;
-}
-static void dlwCopy(DLWriter *pWriter, DLReader *pReader){
- dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader),
- dlrDocid(pReader), dlrDocid(pReader));
-}
-static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){
- char c[VARINT_MAX];
- int n = putVarint(c, iDocid-pWriter->iPrevDocid);
-
- /* Docids must ascend. */
- assert( !pWriter->has_iPrevDocid || iDocid>pWriter->iPrevDocid );
- assert( pWriter->iType==DL_DOCIDS );
-
- dataBufferAppend(pWriter->b, c, n);
- pWriter->iPrevDocid = iDocid;
-#ifndef NDEBUG
- pWriter->has_iPrevDocid = 1;
-#endif
-}
-
-/*******************************************************************/
-/* PLReader is used to read data from a document's position list. As
-** the caller steps through the list, data is cached so that varints
-** only need to be decoded once.
-**
-** plrInit, plrDestroy - create/destroy a reader.
-** plrColumn, plrPosition, plrStartOffset, plrEndOffset - accessors
-** plrAtEnd - at end of stream, only call plrDestroy once true.
-** plrStep - step to the next element.
-*/
-typedef struct PLReader {
- /* These refer to the next position's data. nData will reach 0 when
- ** reading the last position, so plrStep() signals EOF by setting
- ** pData to NULL.
- */
- const char *pData;
- int nData;
-
- DocListType iType;
- int iColumn; /* the last column read */
- int iPosition; /* the last position read */
- int iStartOffset; /* the last start offset read */
- int iEndOffset; /* the last end offset read */
-} PLReader;
-
-static int plrAtEnd(PLReader *pReader){
- return pReader->pData==NULL;
-}
-static int plrColumn(PLReader *pReader){
- assert( !plrAtEnd(pReader) );
- return pReader->iColumn;
-}
-static int plrPosition(PLReader *pReader){
- assert( !plrAtEnd(pReader) );
- return pReader->iPosition;
-}
-static int plrStartOffset(PLReader *pReader){
- assert( !plrAtEnd(pReader) );
- return pReader->iStartOffset;
-}
-static int plrEndOffset(PLReader *pReader){
- assert( !plrAtEnd(pReader) );
- return pReader->iEndOffset;
-}
-static void plrStep(PLReader *pReader){
- int i, n;
-
- assert( !plrAtEnd(pReader) );
-
- if( pReader->nData==0 ){
- pReader->pData = NULL;
- return;
- }
-
- n = getVarint32(pReader->pData, &i);
- if( i==POS_COLUMN ){
- n += getVarint32(pReader->pData+n, &pReader->iColumn);
- pReader->iPosition = 0;
- pReader->iStartOffset = 0;
- n += getVarint32(pReader->pData+n, &i);
- }
- /* Should never see adjacent column changes. */
- assert( i!=POS_COLUMN );
-
- if( i==POS_END ){
- pReader->nData = 0;
- pReader->pData = NULL;
- return;
- }
-
- pReader->iPosition += i-POS_BASE;
- if( pReader->iType==DL_POSITIONS_OFFSETS ){
- n += getVarint32(pReader->pData+n, &i);
- pReader->iStartOffset += i;
- n += getVarint32(pReader->pData+n, &i);
- pReader->iEndOffset = pReader->iStartOffset+i;
- }
- assert( n<=pReader->nData );
- pReader->pData += n;
- pReader->nData -= n;
-}
-
-static void plrInit(PLReader *pReader, DLReader *pDLReader){
- pReader->pData = dlrPosData(pDLReader);
- pReader->nData = dlrPosDataLen(pDLReader);
- pReader->iType = pDLReader->iType;
- pReader->iColumn = 0;
- pReader->iPosition = 0;
- pReader->iStartOffset = 0;
- pReader->iEndOffset = 0;
- plrStep(pReader);
-}
-static void plrDestroy(PLReader *pReader){
- SCRAMBLE(pReader);
-}
-
-/*******************************************************************/
-/* PLWriter is used in constructing a document's position list. As a
-** convenience, if iType is DL_DOCIDS, PLWriter becomes a no-op.
-** PLWriter writes to the associated DLWriter's buffer.
-**
-** plwInit - init for writing a document's poslist.
-** plwDestroy - clear a writer.
-** plwAdd - append position and offset information.
-** plwCopy - copy next position's data from reader to writer.
-** plwTerminate - add any necessary doclist terminator.
-**
-** Calling plwAdd() after plwTerminate() may result in a corrupt
-** doclist.
-*/
-/* TODO(shess) Until we've written the second item, we can cache the
-** first item's information. Then we'd have three states:
-**
-** - initialized with docid, no positions.
-** - docid and one position.
-** - docid and multiple positions.
-**
-** Only the last state needs to actually write to dlw->b, which would
-** be an improvement in the DLCollector case.
-*/
-typedef struct PLWriter {
- DLWriter *dlw;
-
- int iColumn; /* the last column written */
- int iPos; /* the last position written */
- int iOffset; /* the last start offset written */
-} PLWriter;
-
-/* TODO(shess) In the case where the parent is reading these values
-** from a PLReader, we could optimize to a copy if that PLReader has
-** the same type as pWriter.
-*/
-static void plwAdd(PLWriter *pWriter, int iColumn, int iPos,
- int iStartOffset, int iEndOffset){
- /* Worst-case space for POS_COLUMN, iColumn, iPosDelta,
- ** iStartOffsetDelta, and iEndOffsetDelta.
- */
- char c[5*VARINT_MAX];
- int n = 0;
-
- /* Ban plwAdd() after plwTerminate(). */
- assert( pWriter->iPos!=-1 );
-
- if( pWriter->dlw->iType==DL_DOCIDS ) return;
-
- if( iColumn!=pWriter->iColumn ){
- n += putVarint(c+n, POS_COLUMN);
- n += putVarint(c+n, iColumn);
- pWriter->iColumn = iColumn;
- pWriter->iPos = 0;
- pWriter->iOffset = 0;
- }
- assert( iPos>=pWriter->iPos );
- n += putVarint(c+n, POS_BASE+(iPos-pWriter->iPos));
- pWriter->iPos = iPos;
- if( pWriter->dlw->iType==DL_POSITIONS_OFFSETS ){
- assert( iStartOffset>=pWriter->iOffset );
- n += putVarint(c+n, iStartOffset-pWriter->iOffset);
- pWriter->iOffset = iStartOffset;
- assert( iEndOffset>=iStartOffset );
- n += putVarint(c+n, iEndOffset-iStartOffset);
- }
- dataBufferAppend(pWriter->dlw->b, c, n);
-}
-static void plwCopy(PLWriter *pWriter, PLReader *pReader){
- plwAdd(pWriter, plrColumn(pReader), plrPosition(pReader),
- plrStartOffset(pReader), plrEndOffset(pReader));
-}
-static void plwInit(PLWriter *pWriter, DLWriter *dlw, sqlite_int64 iDocid){
- char c[VARINT_MAX];
- int n;
-
- pWriter->dlw = dlw;
-
- /* Docids must ascend. */
- assert( !pWriter->dlw->has_iPrevDocid || iDocid>pWriter->dlw->iPrevDocid );
- n = putVarint(c, iDocid-pWriter->dlw->iPrevDocid);
- dataBufferAppend(pWriter->dlw->b, c, n);
- pWriter->dlw->iPrevDocid = iDocid;
-#ifndef NDEBUG
- pWriter->dlw->has_iPrevDocid = 1;
-#endif
-
- pWriter->iColumn = 0;
- pWriter->iPos = 0;
- pWriter->iOffset = 0;
-}
-/* TODO(shess) Should plwDestroy() also terminate the doclist? But
-** then plwDestroy() would no longer be just a destructor, it would
-** also be doing work, which isn't consistent with the overall idiom.
-** Another option would be for plwAdd() to always append any necessary
-** terminator, so that the output is always correct. But that would
-** add incremental work to the common case with the only benefit being
-** API elegance. Punt for now.
-*/
-static void plwTerminate(PLWriter *pWriter){
- if( pWriter->dlw->iType>DL_DOCIDS ){
- char c[VARINT_MAX];
- int n = putVarint(c, POS_END);
- dataBufferAppend(pWriter->dlw->b, c, n);
- }
-#ifndef NDEBUG
- /* Mark as terminated for assert in plwAdd(). */
- pWriter->iPos = -1;
-#endif
-}
-static void plwDestroy(PLWriter *pWriter){
- SCRAMBLE(pWriter);
-}
-
-/*******************************************************************/
-/* DLCollector wraps PLWriter and DLWriter to provide a
-** dynamically-allocated doclist area to use during tokenization.
-**
-** dlcNew - malloc up and initialize a collector.
-** dlcDelete - destroy a collector and all contained items.
-** dlcAddPos - append position and offset information.
-** dlcAddDoclist - add the collected doclist to the given buffer.
-** dlcNext - terminate the current document and open another.
-*/
-typedef struct DLCollector {
- DataBuffer b;
- DLWriter dlw;
- PLWriter plw;
-} DLCollector;
-
-/* TODO(shess) This could also be done by calling plwTerminate() and
-** dataBufferAppend(). I tried that, expecting nominal performance
-** differences, but it seemed to pretty reliably be worth 1% to code
-** it this way. I suspect it is the incremental malloc overhead (some
-** percentage of the plwTerminate() calls will cause a realloc), so
-** this might be worth revisiting if the DataBuffer implementation
-** changes.
-*/
-static void dlcAddDoclist(DLCollector *pCollector, DataBuffer *b){
- if( pCollector->dlw.iType>DL_DOCIDS ){
- char c[VARINT_MAX];
- int n = putVarint(c, POS_END);
- dataBufferAppend2(b, pCollector->b.pData, pCollector->b.nData, c, n);
- }else{
- dataBufferAppend(b, pCollector->b.pData, pCollector->b.nData);
- }
-}
-static void dlcNext(DLCollector *pCollector, sqlite_int64 iDocid){
- plwTerminate(&pCollector->plw);
- plwDestroy(&pCollector->plw);
- plwInit(&pCollector->plw, &pCollector->dlw, iDocid);
-}
-static void dlcAddPos(DLCollector *pCollector, int iColumn, int iPos,
- int iStartOffset, int iEndOffset){
- plwAdd(&pCollector->plw, iColumn, iPos, iStartOffset, iEndOffset);
-}
-
-static DLCollector *dlcNew(sqlite_int64 iDocid, DocListType iType){
- DLCollector *pCollector = sqlite3_malloc(sizeof(DLCollector));
- dataBufferInit(&pCollector->b, 0);
- dlwInit(&pCollector->dlw, iType, &pCollector->b);
- plwInit(&pCollector->plw, &pCollector->dlw, iDocid);
- return pCollector;
-}
-static void dlcDelete(DLCollector *pCollector){
- plwDestroy(&pCollector->plw);
- dlwDestroy(&pCollector->dlw);
- dataBufferDestroy(&pCollector->b);
- SCRAMBLE(pCollector);
- sqlite3_free(pCollector);
-}
-
-
-/* Copy the doclist data of iType in pData/nData into *out, trimming
-** unnecessary data as we go. Only columns matching iColumn are
-** copied, all columns copied if iColumn is -1. Elements with no
-** matching columns are dropped. The output is an iOutType doclist.
-*/
-/* NOTE(shess) This code is only valid after all doclists are merged.
-** If this is run before merges, then doclist items which represent
-** deletion will be trimmed, and will thus not effect a deletion
-** during the merge.
-*/
-static void docListTrim(DocListType iType, const char *pData, int nData,
- int iColumn, DocListType iOutType, DataBuffer *out){
- DLReader dlReader;
- DLWriter dlWriter;
-
- assert( iOutType<=iType );
-
- dlrInit(&dlReader, iType, pData, nData);
- dlwInit(&dlWriter, iOutType, out);
-
- while( !dlrAtEnd(&dlReader) ){
- PLReader plReader;
- PLWriter plWriter;
- int match = 0;
-
- plrInit(&plReader, &dlReader);
-
- while( !plrAtEnd(&plReader) ){
- if( iColumn==-1 || plrColumn(&plReader)==iColumn ){
- if( !match ){
- plwInit(&plWriter, &dlWriter, dlrDocid(&dlReader));
- match = 1;
- }
- plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader),
- plrStartOffset(&plReader), plrEndOffset(&plReader));
- }
- plrStep(&plReader);
- }
- if( match ){
- plwTerminate(&plWriter);
- plwDestroy(&plWriter);
- }
-
- plrDestroy(&plReader);
- dlrStep(&dlReader);
- }
- dlwDestroy(&dlWriter);
- dlrDestroy(&dlReader);
-}
-
-/* Used by docListMerge() to keep doclists in the ascending order by
-** docid, then ascending order by age (so the newest comes first).
-*/
-typedef struct OrderedDLReader {
- DLReader *pReader;
-
- /* TODO(shess) If we assume that docListMerge pReaders is ordered by
- ** age (which we do), then we could use pReader comparisons to break
- ** ties.
- */
- int idx;
-} OrderedDLReader;
-
-/* Order eof to end, then by docid asc, idx desc. */
-static int orderedDLReaderCmp(OrderedDLReader *r1, OrderedDLReader *r2){
- if( dlrAtEnd(r1->pReader) ){
- if( dlrAtEnd(r2->pReader) ) return 0; /* Both atEnd(). */
- return 1; /* Only r1 atEnd(). */
- }
- if( dlrAtEnd(r2->pReader) ) return -1; /* Only r2 atEnd(). */
-
- if( dlrDocid(r1->pReader)pReader) ) return -1;
- if( dlrDocid(r1->pReader)>dlrDocid(r2->pReader) ) return 1;
-
- /* Descending on idx. */
- return r2->idx-r1->idx;
-}
-
-/* Bubble p[0] to appropriate place in p[1..n-1]. Assumes that
-** p[1..n-1] is already sorted.
-*/
-/* TODO(shess) Is this frequent enough to warrant a binary search?
-** Before implementing that, instrument the code to check. In most
-** current usage, I expect that p[0] will be less than p[1] a very
-** high proportion of the time.
-*/
-static void orderedDLReaderReorder(OrderedDLReader *p, int n){
- while( n>1 && orderedDLReaderCmp(p, p+1)>0 ){
- OrderedDLReader tmp = p[0];
- p[0] = p[1];
- p[1] = tmp;
- n--;
- p++;
- }
-}
-
-/* Given an array of doclist readers, merge their doclist elements
-** into out in sorted order (by docid), dropping elements from older
-** readers when there is a duplicate docid. pReaders is assumed to be
-** ordered by age, oldest first.
-*/
-/* TODO(shess) nReaders must be <= MERGE_COUNT. This should probably
-** be fixed.
-*/
-static void docListMerge(DataBuffer *out,
- DLReader *pReaders, int nReaders){
- OrderedDLReader readers[MERGE_COUNT];
- DLWriter writer;
- int i, n;
- const char *pStart = 0;
- int nStart = 0;
- sqlite_int64 iFirstDocid = 0, iLastDocid = 0;
-
- assert( nReaders>0 );
- if( nReaders==1 ){
- dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders));
- return;
- }
-
- assert( nReaders<=MERGE_COUNT );
- n = 0;
- for(i=0; i0 ){
- orderedDLReaderReorder(readers+i, nReaders-i);
- }
-
- dlwInit(&writer, pReaders[0].iType, out);
- while( !dlrAtEnd(readers[0].pReader) ){
- sqlite_int64 iDocid = dlrDocid(readers[0].pReader);
-
- /* If this is a continuation of the current buffer to copy, extend
- ** that buffer. memcpy() seems to be more efficient if it has a
- ** lots of data to copy.
- */
- if( dlrDocData(readers[0].pReader)==pStart+nStart ){
- nStart += dlrDocDataBytes(readers[0].pReader);
- }else{
- if( pStart!=0 ){
- dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid);
- }
- pStart = dlrDocData(readers[0].pReader);
- nStart = dlrDocDataBytes(readers[0].pReader);
- iFirstDocid = iDocid;
- }
- iLastDocid = iDocid;
- dlrStep(readers[0].pReader);
-
- /* Drop all of the older elements with the same docid. */
- for(i=1; i0 ){
- orderedDLReaderReorder(readers+i, nReaders-i);
- }
- }
-
- /* Copy over any remaining elements. */
- if( nStart>0 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid);
- dlwDestroy(&writer);
-}
-
-/* Helper function for posListUnion(). Compares the current position
-** between left and right, returning as standard C idiom of <0 if
-** left0 if left>right, and 0 if left==right. "End" always
-** compares greater.
-*/
-static int posListCmp(PLReader *pLeft, PLReader *pRight){
- assert( pLeft->iType==pRight->iType );
- if( pLeft->iType==DL_DOCIDS ) return 0;
-
- if( plrAtEnd(pLeft) ) return plrAtEnd(pRight) ? 0 : 1;
- if( plrAtEnd(pRight) ) return -1;
-
- if( plrColumn(pLeft)plrColumn(pRight) ) return 1;
-
- if( plrPosition(pLeft)plrPosition(pRight) ) return 1;
- if( pLeft->iType==DL_POSITIONS ) return 0;
-
- if( plrStartOffset(pLeft)plrStartOffset(pRight) ) return 1;
-
- if( plrEndOffset(pLeft)plrEndOffset(pRight) ) return 1;
-
- return 0;
-}
-
-/* Write the union of position lists in pLeft and pRight to pOut.
-** "Union" in this case meaning "All unique position tuples". Should
-** work with any doclist type, though both inputs and the output
-** should be the same type.
-*/
-static void posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){
- PLReader left, right;
- PLWriter writer;
-
- assert( dlrDocid(pLeft)==dlrDocid(pRight) );
- assert( pLeft->iType==pRight->iType );
- assert( pLeft->iType==pOut->iType );
-
- plrInit(&left, pLeft);
- plrInit(&right, pRight);
- plwInit(&writer, pOut, dlrDocid(pLeft));
-
- while( !plrAtEnd(&left) || !plrAtEnd(&right) ){
- int c = posListCmp(&left, &right);
- if( c<0 ){
- plwCopy(&writer, &left);
- plrStep(&left);
- }else if( c>0 ){
- plwCopy(&writer, &right);
- plrStep(&right);
- }else{
- plwCopy(&writer, &left);
- plrStep(&left);
- plrStep(&right);
- }
- }
-
- plwTerminate(&writer);
- plwDestroy(&writer);
- plrDestroy(&left);
- plrDestroy(&right);
-}
-
-/* Write the union of doclists in pLeft and pRight to pOut. For
-** docids in common between the inputs, the union of the position
-** lists is written. Inputs and outputs are always type DL_DEFAULT.
-*/
-static void docListUnion(
- const char *pLeft, int nLeft,
- const char *pRight, int nRight,
- DataBuffer *pOut /* Write the combined doclist here */
-){
- DLReader left, right;
- DLWriter writer;
-
- if( nLeft==0 ){
- if( nRight!=0) dataBufferAppend(pOut, pRight, nRight);
- return;
- }
- if( nRight==0 ){
- dataBufferAppend(pOut, pLeft, nLeft);
- return;
- }
-
- dlrInit(&left, DL_DEFAULT, pLeft, nLeft);
- dlrInit(&right, DL_DEFAULT, pRight, nRight);
- dlwInit(&writer, DL_DEFAULT, pOut);
-
- while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){
- if( dlrAtEnd(&right) ){
- dlwCopy(&writer, &left);
- dlrStep(&left);
- }else if( dlrAtEnd(&left) ){
- dlwCopy(&writer, &right);
- dlrStep(&right);
- }else if( dlrDocid(&left)dlrDocid(&right) ){
- dlwCopy(&writer, &right);
- dlrStep(&right);
- }else{
- posListUnion(&left, &right, &writer);
- dlrStep(&left);
- dlrStep(&right);
- }
- }
-
- dlrDestroy(&left);
- dlrDestroy(&right);
- dlwDestroy(&writer);
-}
-
-/* pLeft and pRight are DLReaders positioned to the same docid.
-**
-** If there are no instances in pLeft or pRight where the position
-** of pLeft is one less than the position of pRight, then this
-** routine adds nothing to pOut.
-**
-** If there are one or more instances where positions from pLeft
-** are exactly one less than positions from pRight, then add a new
-** document record to pOut. If pOut wants to hold positions, then
-** include the positions from pRight that are one more than a
-** position in pLeft. In other words: pRight.iPos==pLeft.iPos+1.
-*/
-static void posListPhraseMerge(DLReader *pLeft, DLReader *pRight,
- DLWriter *pOut){
- PLReader left, right;
- PLWriter writer;
- int match = 0;
-
- assert( dlrDocid(pLeft)==dlrDocid(pRight) );
- assert( pOut->iType!=DL_POSITIONS_OFFSETS );
-
- plrInit(&left, pLeft);
- plrInit(&right, pRight);
-
- while( !plrAtEnd(&left) && !plrAtEnd(&right) ){
- if( plrColumn(&left)plrColumn(&right) ){
- plrStep(&right);
- }else if( plrPosition(&left)+1plrPosition(&right) ){
- plrStep(&right);
- }else{
- if( !match ){
- plwInit(&writer, pOut, dlrDocid(pLeft));
- match = 1;
- }
- plwAdd(&writer, plrColumn(&right), plrPosition(&right), 0, 0);
- plrStep(&left);
- plrStep(&right);
- }
- }
-
- if( match ){
- plwTerminate(&writer);
- plwDestroy(&writer);
- }
-
- plrDestroy(&left);
- plrDestroy(&right);
-}
-
-/* We have two doclists with positions: pLeft and pRight.
-** Write the phrase intersection of these two doclists into pOut.
-**
-** A phrase intersection means that two documents only match
-** if pLeft.iPos+1==pRight.iPos.
-**
-** iType controls the type of data written to pOut. If iType is
-** DL_POSITIONS, the positions are those from pRight.
-*/
-static void docListPhraseMerge(
- const char *pLeft, int nLeft,
- const char *pRight, int nRight,
- DocListType iType,
- DataBuffer *pOut /* Write the combined doclist here */
-){
- DLReader left, right;
- DLWriter writer;
-
- if( nLeft==0 || nRight==0 ) return;
-
- assert( iType!=DL_POSITIONS_OFFSETS );
-
- dlrInit(&left, DL_POSITIONS, pLeft, nLeft);
- dlrInit(&right, DL_POSITIONS, pRight, nRight);
- dlwInit(&writer, iType, pOut);
-
- while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){
- if( dlrDocid(&left) one AND (two OR three)
- * [one OR two three] ==> (one OR two) AND three
- *
- * A "-" before a term matches all entries that lack that term.
- * The "-" must occur immediately before the term with in intervening
- * space. This is how the search engines do it.
- *
- * A NOT term cannot be the right-hand operand of an OR. If this
- * occurs in the query string, the NOT is ignored:
- *
- * [one OR -two] ==> one OR two
- *
- */
-typedef struct Query {
- fulltext_vtab *pFts; /* The full text index */
- int nTerms; /* Number of terms in the query */
- QueryTerm *pTerms; /* Array of terms. Space obtained from malloc() */
- int nextIsOr; /* Set the isOr flag on the next inserted term */
- int nextColumn; /* Next word parsed must be in this column */
- int dfltColumn; /* The default column */
-} Query;
-
-
-/*
-** An instance of the following structure keeps track of generated
-** matching-word offset information and snippets.
-*/
-typedef struct Snippet {
- int nMatch; /* Total number of matches */
- int nAlloc; /* Space allocated for aMatch[] */
- struct snippetMatch { /* One entry for each matching term */
- char snStatus; /* Status flag for use while constructing snippets */
- short int iCol; /* The column that contains the match */
- short int iTerm; /* The index in Query.pTerms[] of the matching term */
- short int nByte; /* Number of bytes in the term */
- int iStart; /* The offset to the first character of the term */
- } *aMatch; /* Points to space obtained from malloc */
- char *zOffset; /* Text rendering of aMatch[] */
- int nOffset; /* strlen(zOffset) */
- char *zSnippet; /* Snippet text */
- int nSnippet; /* strlen(zSnippet) */
-} Snippet;
-
-
-typedef enum QueryType {
- QUERY_GENERIC, /* table scan */
- QUERY_ROWID, /* lookup by rowid */
- QUERY_FULLTEXT /* QUERY_FULLTEXT + [i] is a full-text search for column i*/
-} QueryType;
-
-typedef enum fulltext_statement {
- CONTENT_INSERT_STMT,
- CONTENT_SELECT_STMT,
- CONTENT_UPDATE_STMT,
- CONTENT_DELETE_STMT,
- CONTENT_EXISTS_STMT,
-
- BLOCK_INSERT_STMT,
- BLOCK_SELECT_STMT,
- BLOCK_DELETE_STMT,
- BLOCK_DELETE_ALL_STMT,
-
- SEGDIR_MAX_INDEX_STMT,
- SEGDIR_SET_STMT,
- SEGDIR_SELECT_LEVEL_STMT,
- SEGDIR_SPAN_STMT,
- SEGDIR_DELETE_STMT,
- SEGDIR_SELECT_SEGMENT_STMT,
- SEGDIR_SELECT_ALL_STMT,
- SEGDIR_DELETE_ALL_STMT,
- SEGDIR_COUNT_STMT,
-
- MAX_STMT /* Always at end! */
-} fulltext_statement;
-
-/* These must exactly match the enum above. */
-/* TODO(shess): Is there some risk that a statement will be used in two
-** cursors at once, e.g. if a query joins a virtual table to itself?
-** If so perhaps we should move some of these to the cursor object.
-*/
-static const char *const fulltext_zStatement[MAX_STMT] = {
- /* CONTENT_INSERT */ NULL, /* generated in contentInsertStatement() */
- /* CONTENT_SELECT */ "select * from %_content where rowid = ?",
- /* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */
- /* CONTENT_DELETE */ "delete from %_content where rowid = ?",
- /* CONTENT_EXISTS */ "select rowid from %_content limit 1",
-
- /* BLOCK_INSERT */ "insert into %_segments values (?)",
- /* BLOCK_SELECT */ "select block from %_segments where rowid = ?",
- /* BLOCK_DELETE */ "delete from %_segments where rowid between ? and ?",
- /* BLOCK_DELETE_ALL */ "delete from %_segments",
-
- /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?",
- /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)",
- /* SEGDIR_SELECT_LEVEL */
- "select start_block, leaves_end_block, root from %_segdir "
- " where level = ? order by idx",
- /* SEGDIR_SPAN */
- "select min(start_block), max(end_block) from %_segdir "
- " where level = ? and start_block <> 0",
- /* SEGDIR_DELETE */ "delete from %_segdir where level = ?",
-
- /* NOTE(shess): The first three results of the following two
- ** statements must match.
- */
- /* SEGDIR_SELECT_SEGMENT */
- "select start_block, leaves_end_block, root from %_segdir "
- " where level = ? and idx = ?",
- /* SEGDIR_SELECT_ALL */
- "select start_block, leaves_end_block, root from %_segdir "
- " order by level desc, idx asc",
- /* SEGDIR_DELETE_ALL */ "delete from %_segdir",
- /* SEGDIR_COUNT */ "select count(*), ifnull(max(level),0) from %_segdir",
-};
-
-/*
-** A connection to a fulltext index is an instance of the following
-** structure. The xCreate and xConnect methods create an instance
-** of this structure and xDestroy and xDisconnect free that instance.
-** All other methods receive a pointer to the structure as one of their
-** arguments.
-*/
-struct fulltext_vtab {
- sqlite3_vtab base; /* Base class used by SQLite core */
- sqlite3 *db; /* The database connection */
- const char *zDb; /* logical database name */
- const char *zName; /* virtual table name */
- int nColumn; /* number of columns in virtual table */
- char **azColumn; /* column names. malloced */
- char **azContentColumn; /* column names in content table; malloced */
- sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
-
- /* Precompiled statements which we keep as long as the table is
- ** open.
- */
- sqlite3_stmt *pFulltextStatements[MAX_STMT];
-
- /* Precompiled statements used for segment merges. We run a
- ** separate select across the leaf level of each tree being merged.
- */
- sqlite3_stmt *pLeafSelectStmts[MERGE_COUNT];
- /* The statement used to prepare pLeafSelectStmts. */
-#define LEAF_SELECT \
- "select block from %_segments where rowid between ? and ? order by rowid"
-
- /* These buffer pending index updates during transactions.
- ** nPendingData estimates the memory size of the pending data. It
- ** doesn't include the hash-bucket overhead, nor any malloc
- ** overhead. When nPendingData exceeds kPendingThreshold, the
- ** buffer is flushed even before the transaction closes.
- ** pendingTerms stores the data, and is only valid when nPendingData
- ** is >=0 (nPendingData<0 means pendingTerms has not been
- ** initialized). iPrevDocid is the last docid written, used to make
- ** certain we're inserting in sorted order.
- */
- int nPendingData;
-#define kPendingThreshold (1*1024*1024)
- sqlite_int64 iPrevDocid;
- fts2Hash pendingTerms;
-};
-
-/*
-** When the core wants to do a query, it create a cursor using a
-** call to xOpen. This structure is an instance of a cursor. It
-** is destroyed by xClose.
-*/
-typedef struct fulltext_cursor {
- sqlite3_vtab_cursor base; /* Base class used by SQLite core */
- QueryType iCursorType; /* Copy of sqlite3_index_info.idxNum */
- sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
- int eof; /* True if at End Of Results */
- Query q; /* Parsed query string */
- Snippet snippet; /* Cached snippet for the current row */
- int iColumn; /* Column being searched */
- DataBuffer result; /* Doclist results from fulltextQuery */
- DLReader reader; /* Result reader if result not empty */
-} fulltext_cursor;
-
-static struct fulltext_vtab *cursor_vtab(fulltext_cursor *c){
- return (fulltext_vtab *) c->base.pVtab;
-}
-
-static const sqlite3_module fts2Module; /* forward declaration */
-
-/* Return a dynamically generated statement of the form
- * insert into %_content (rowid, ...) values (?, ...)
- */
-static const char *contentInsertStatement(fulltext_vtab *v){
- StringBuffer sb;
- int i;
-
- initStringBuffer(&sb);
- append(&sb, "insert into %_content (rowid, ");
- appendList(&sb, v->nColumn, v->azContentColumn);
- append(&sb, ") values (?");
- for(i=0; inColumn; ++i)
- append(&sb, ", ?");
- append(&sb, ")");
- return stringBufferData(&sb);
-}
-
-/* Return a dynamically generated statement of the form
- * update %_content set [col_0] = ?, [col_1] = ?, ...
- * where rowid = ?
- */
-static const char *contentUpdateStatement(fulltext_vtab *v){
- StringBuffer sb;
- int i;
-
- initStringBuffer(&sb);
- append(&sb, "update %_content set ");
- for(i=0; inColumn; ++i) {
- if( i>0 ){
- append(&sb, ", ");
- }
- append(&sb, v->azContentColumn[i]);
- append(&sb, " = ?");
- }
- append(&sb, " where rowid = ?");
- return stringBufferData(&sb);
-}
-
-/* Puts a freshly-prepared statement determined by iStmt in *ppStmt.
-** If the indicated statement has never been prepared, it is prepared
-** and cached, otherwise the cached version is reset.
-*/
-static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt,
- sqlite3_stmt **ppStmt){
- assert( iStmtpFulltextStatements[iStmt]==NULL ){
- const char *zStmt;
- int rc;
- switch( iStmt ){
- case CONTENT_INSERT_STMT:
- zStmt = contentInsertStatement(v); break;
- case CONTENT_UPDATE_STMT:
- zStmt = contentUpdateStatement(v); break;
- default:
- zStmt = fulltext_zStatement[iStmt];
- }
- rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
- zStmt);
- if( zStmt != fulltext_zStatement[iStmt]) sqlite3_free((void *) zStmt);
- if( rc!=SQLITE_OK ) return rc;
- } else {
- int rc = sqlite3_reset(v->pFulltextStatements[iStmt]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- *ppStmt = v->pFulltextStatements[iStmt];
- return SQLITE_OK;
-}
-
-/* Like sqlite3_step(), but convert SQLITE_DONE to SQLITE_OK and
-** SQLITE_ROW to SQLITE_ERROR. Useful for statements like UPDATE,
-** where we expect no results.
-*/
-static int sql_single_step(sqlite3_stmt *s){
- int rc = sqlite3_step(s);
- return (rc==SQLITE_DONE) ? SQLITE_OK : rc;
-}
-
-/* Like sql_get_statement(), but for special replicated LEAF_SELECT
-** statements. idx -1 is a special case for an uncached version of
-** the statement (used in the optimize implementation).
-*/
-/* TODO(shess) Write version for generic statements and then share
-** that between the cached-statement functions.
-*/
-static int sql_get_leaf_statement(fulltext_vtab *v, int idx,
- sqlite3_stmt **ppStmt){
- assert( idx>=-1 && idxdb, v->zDb, v->zName, ppStmt, LEAF_SELECT);
- }else if( v->pLeafSelectStmts[idx]==NULL ){
- int rc = sql_prepare(v->db, v->zDb, v->zName, &v->pLeafSelectStmts[idx],
- LEAF_SELECT);
- if( rc!=SQLITE_OK ) return rc;
- }else{
- int rc = sqlite3_reset(v->pLeafSelectStmts[idx]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- *ppStmt = v->pLeafSelectStmts[idx];
- return SQLITE_OK;
-}
-
-/* insert into %_content (rowid, ...) values ([rowid], [pValues]) */
-static int content_insert(fulltext_vtab *v, sqlite3_value *rowid,
- sqlite3_value **pValues){
- sqlite3_stmt *s;
- int i;
- int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_value(s, 1, rowid);
- if( rc!=SQLITE_OK ) return rc;
-
- for(i=0; inColumn; ++i){
- rc = sqlite3_bind_value(s, 2+i, pValues[i]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- return sql_single_step(s);
-}
-
-/* update %_content set col0 = pValues[0], col1 = pValues[1], ...
- * where rowid = [iRowid] */
-static int content_update(fulltext_vtab *v, sqlite3_value **pValues,
- sqlite_int64 iRowid){
- sqlite3_stmt *s;
- int i;
- int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- for(i=0; inColumn; ++i){
- rc = sqlite3_bind_value(s, 1+i, pValues[i]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- rc = sqlite3_bind_int64(s, 1+v->nColumn, iRowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step(s);
-}
-
-static void freeStringArray(int nString, const char **pString){
- int i;
-
- for (i=0 ; i < nString ; ++i) {
- if( pString[i]!=NULL ) sqlite3_free((void *) pString[i]);
- }
- sqlite3_free((void *) pString);
-}
-
-/* select * from %_content where rowid = [iRow]
- * The caller must delete the returned array and all strings in it.
- * null fields will be NULL in the returned array.
- *
- * TODO: Perhaps we should return pointer/length strings here for consistency
- * with other code which uses pointer/length. */
-static int content_select(fulltext_vtab *v, sqlite_int64 iRow,
- const char ***pValues){
- sqlite3_stmt *s;
- const char **values;
- int i;
- int rc;
-
- *pValues = NULL;
-
- rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- if( rc!=SQLITE_ROW ) return rc;
-
- values = (const char **) sqlite3_malloc(v->nColumn * sizeof(const char *));
- for(i=0; inColumn; ++i){
- if( sqlite3_column_type(s, i)==SQLITE_NULL ){
- values[i] = NULL;
- }else{
- values[i] = string_dup((char*)sqlite3_column_text(s, i));
- }
- }
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ){
- *pValues = values;
- return SQLITE_OK;
- }
-
- freeStringArray(v->nColumn, values);
- return rc;
-}
-
-/* delete from %_content where rowid = [iRow ] */
-static int content_delete(fulltext_vtab *v, sqlite_int64 iRow){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step(s);
-}
-
-/* Returns SQLITE_ROW if any rows exist in %_content, SQLITE_DONE if
-** no rows exist, and any error in case of failure.
-*/
-static int content_exists(fulltext_vtab *v){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, CONTENT_EXISTS_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- if( rc!=SQLITE_ROW ) return rc;
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ) return SQLITE_ROW;
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- return rc;
-}
-
-/* insert into %_segments values ([pData])
-** returns assigned rowid in *piBlockid
-*/
-static int block_insert(fulltext_vtab *v, const char *pData, int nData,
- sqlite_int64 *piBlockid){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, BLOCK_INSERT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_blob(s, 1, pData, nData, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- if( rc!=SQLITE_DONE ) return rc;
-
- *piBlockid = sqlite3_last_insert_rowid(v->db);
- return SQLITE_OK;
-}
-
-/* delete from %_segments
-** where rowid between [iStartBlockid] and [iEndBlockid]
-**
-** Deletes the range of blocks, inclusive, used to delete the blocks
-** which form a segment.
-*/
-static int block_delete(fulltext_vtab *v,
- sqlite_int64 iStartBlockid, sqlite_int64 iEndBlockid){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, BLOCK_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iStartBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, iEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step(s);
-}
-
-/* Returns SQLITE_ROW with *pidx set to the maximum segment idx found
-** at iLevel. Returns SQLITE_DONE if there are no segments at
-** iLevel. Otherwise returns an error.
-*/
-static int segdir_max_index(fulltext_vtab *v, int iLevel, int *pidx){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, SEGDIR_MAX_INDEX_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 1, iLevel);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- /* Should always get at least one row due to how max() works. */
- if( rc==SQLITE_DONE ) return SQLITE_DONE;
- if( rc!=SQLITE_ROW ) return rc;
-
- /* NULL means that there were no inputs to max(). */
- if( SQLITE_NULL==sqlite3_column_type(s, 0) ){
- rc = sqlite3_step(s);
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- return rc;
- }
-
- *pidx = sqlite3_column_int(s, 0);
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- if( rc!=SQLITE_DONE ) return rc;
- return SQLITE_ROW;
-}
-
-/* insert into %_segdir values (
-** [iLevel], [idx],
-** [iStartBlockid], [iLeavesEndBlockid], [iEndBlockid],
-** [pRootData]
-** )
-*/
-static int segdir_set(fulltext_vtab *v, int iLevel, int idx,
- sqlite_int64 iStartBlockid,
- sqlite_int64 iLeavesEndBlockid,
- sqlite_int64 iEndBlockid,
- const char *pRootData, int nRootData){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, SEGDIR_SET_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 1, iLevel);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 2, idx);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 3, iStartBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 4, iLeavesEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 5, iEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_blob(s, 6, pRootData, nRootData, SQLITE_STATIC);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step(s);
-}
-
-/* Queries %_segdir for the block span of the segments in level
-** iLevel. Returns SQLITE_DONE if there are no blocks for iLevel,
-** SQLITE_ROW if there are blocks, else an error.
-*/
-static int segdir_span(fulltext_vtab *v, int iLevel,
- sqlite_int64 *piStartBlockid,
- sqlite_int64 *piEndBlockid){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, SEGDIR_SPAN_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 1, iLevel);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ) return SQLITE_DONE; /* Should never happen */
- if( rc!=SQLITE_ROW ) return rc;
-
- /* This happens if all segments at this level are entirely inline. */
- if( SQLITE_NULL==sqlite3_column_type(s, 0) ){
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- int rc2 = sqlite3_step(s);
- if( rc2==SQLITE_ROW ) return SQLITE_ERROR;
- return rc2;
- }
-
- *piStartBlockid = sqlite3_column_int64(s, 0);
- *piEndBlockid = sqlite3_column_int64(s, 1);
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- if( rc!=SQLITE_DONE ) return rc;
- return SQLITE_ROW;
-}
-
-/* Delete the segment blocks and segment directory records for all
-** segments at iLevel.
-*/
-static int segdir_delete(fulltext_vtab *v, int iLevel){
- sqlite3_stmt *s;
- sqlite_int64 iStartBlockid, iEndBlockid;
- int rc = segdir_span(v, iLevel, &iStartBlockid, &iEndBlockid);
- if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc;
-
- if( rc==SQLITE_ROW ){
- rc = block_delete(v, iStartBlockid, iEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- /* Delete the segment directory itself. */
- rc = sql_get_statement(v, SEGDIR_DELETE_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iLevel);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step(s);
-}
-
-/* Delete entire fts index, SQLITE_OK on success, relevant error on
-** failure.
-*/
-static int segdir_delete_all(fulltext_vtab *v){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, SEGDIR_DELETE_ALL_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_single_step(s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sql_get_statement(v, BLOCK_DELETE_ALL_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- return sql_single_step(s);
-}
-
-/* Returns SQLITE_OK with *pnSegments set to the number of entries in
-** %_segdir and *piMaxLevel set to the highest level which has a
-** segment. Otherwise returns the SQLite error which caused failure.
-*/
-static int segdir_count(fulltext_vtab *v, int *pnSegments, int *piMaxLevel){
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, SEGDIR_COUNT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- /* TODO(shess): This case should not be possible? Should stronger
- ** measures be taken if it happens?
- */
- if( rc==SQLITE_DONE ){
- *pnSegments = 0;
- *piMaxLevel = 0;
- return SQLITE_OK;
- }
- if( rc!=SQLITE_ROW ) return rc;
-
- *pnSegments = sqlite3_column_int(s, 0);
- *piMaxLevel = sqlite3_column_int(s, 1);
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ) return SQLITE_OK;
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- return rc;
-}
-
-/* TODO(shess) clearPendingTerms() is far down the file because
-** writeZeroSegment() is far down the file because LeafWriter is far
-** down the file. Consider refactoring the code to move the non-vtab
-** code above the vtab code so that we don't need this forward
-** reference.
-*/
-static int clearPendingTerms(fulltext_vtab *v);
-
-/*
-** Free the memory used to contain a fulltext_vtab structure.
-*/
-static void fulltext_vtab_destroy(fulltext_vtab *v){
- int iStmt, i;
-
- TRACE(("FTS2 Destroy %p\n", v));
- for( iStmt=0; iStmtpFulltextStatements[iStmt]!=NULL ){
- sqlite3_finalize(v->pFulltextStatements[iStmt]);
- v->pFulltextStatements[iStmt] = NULL;
- }
- }
-
- for( i=0; ipLeafSelectStmts[i]!=NULL ){
- sqlite3_finalize(v->pLeafSelectStmts[i]);
- v->pLeafSelectStmts[i] = NULL;
- }
- }
-
- if( v->pTokenizer!=NULL ){
- v->pTokenizer->pModule->xDestroy(v->pTokenizer);
- v->pTokenizer = NULL;
- }
-
- clearPendingTerms(v);
-
- sqlite3_free(v->azColumn);
- for(i = 0; i < v->nColumn; ++i) {
- sqlite3_free(v->azContentColumn[i]);
- }
- sqlite3_free(v->azContentColumn);
- sqlite3_free(v);
-}
-
-/*
-** Token types for parsing the arguments to xConnect or xCreate.
-*/
-#define TOKEN_EOF 0 /* End of file */
-#define TOKEN_SPACE 1 /* Any kind of whitespace */
-#define TOKEN_ID 2 /* An identifier */
-#define TOKEN_STRING 3 /* A string literal */
-#define TOKEN_PUNCT 4 /* A single punctuation character */
-
-/*
-** If X is a character that can be used in an identifier then
-** IdChar(X) will be true. Otherwise it is false.
-**
-** For ASCII, any character with the high-order bit set is
-** allowed in an identifier. For 7-bit characters,
-** sqlite3IsIdChar[X] must be 1.
-**
-** Ticket #1066. the SQL standard does not allow '$' in the
-** middle of identfiers. But many SQL implementations do.
-** SQLite will allow '$' in identifiers for compatibility.
-** But the feature is undocumented.
-*/
-static const char isIdChar[] = {
-/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
- 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
-};
-#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && isIdChar[c-0x20]))
-
-
-/*
-** Return the length of the token that begins at z[0].
-** Store the token type in *tokenType before returning.
-*/
-static int getToken(const char *z, int *tokenType){
- int i, c;
- switch( *z ){
- case 0: {
- *tokenType = TOKEN_EOF;
- return 0;
- }
- case ' ': case '\t': case '\n': case '\f': case '\r': {
- for(i=1; safe_isspace(z[i]); i++){}
- *tokenType = TOKEN_SPACE;
- return i;
- }
- case '`':
- case '\'':
- case '"': {
- int delim = z[0];
- for(i=1; (c=z[i])!=0; i++){
- if( c==delim ){
- if( z[i+1]==delim ){
- i++;
- }else{
- break;
- }
- }
- }
- *tokenType = TOKEN_STRING;
- return i + (c!=0);
- }
- case '[': {
- for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
- *tokenType = TOKEN_ID;
- return i;
- }
- default: {
- if( !IdChar(*z) ){
- break;
- }
- for(i=1; IdChar(z[i]); i++){}
- *tokenType = TOKEN_ID;
- return i;
- }
- }
- *tokenType = TOKEN_PUNCT;
- return 1;
-}
-
-/*
-** A token extracted from a string is an instance of the following
-** structure.
-*/
-typedef struct Token {
- const char *z; /* Pointer to token text. Not '\000' terminated */
- short int n; /* Length of the token text in bytes. */
-} Token;
-
-/*
-** Given a input string (which is really one of the argv[] parameters
-** passed into xConnect or xCreate) split the string up into tokens.
-** Return an array of pointers to '\000' terminated strings, one string
-** for each non-whitespace token.
-**
-** The returned array is terminated by a single NULL pointer.
-**
-** Space to hold the returned array is obtained from a single
-** malloc and should be freed by passing the return value to free().
-** The individual strings within the token list are all a part of
-** the single memory allocation and will all be freed at once.
-*/
-static char **tokenizeString(const char *z, int *pnToken){
- int nToken = 0;
- Token *aToken = sqlite3_malloc( strlen(z) * sizeof(aToken[0]) );
- int n = 1;
- int e, i;
- int totalSize = 0;
- char **azToken;
- char *zCopy;
- while( n>0 ){
- n = getToken(z, &e);
- if( e!=TOKEN_SPACE ){
- aToken[nToken].z = z;
- aToken[nToken].n = n;
- nToken++;
- totalSize += n+1;
- }
- z += n;
- }
- azToken = (char**)sqlite3_malloc( nToken*sizeof(char*) + totalSize );
- zCopy = (char*)&azToken[nToken];
- nToken--;
- for(i=0; i=0 ){
- azIn[j] = azIn[i];
- }
- j++;
- }
- }
- azIn[j] = 0;
- }
-}
-
-
-/*
-** Find the first alphanumeric token in the string zIn. Null-terminate
-** this token. Remove any quotation marks. And return a pointer to
-** the result.
-*/
-static char *firstToken(char *zIn, char **pzTail){
- int n, ttype;
- while(1){
- n = getToken(zIn, &ttype);
- if( ttype==TOKEN_SPACE ){
- zIn += n;
- }else if( ttype==TOKEN_EOF ){
- *pzTail = zIn;
- return 0;
- }else{
- zIn[n] = 0;
- *pzTail = &zIn[1];
- dequoteString(zIn);
- return zIn;
- }
- }
- /*NOTREACHED*/
-}
-
-/* Return true if...
-**
-** * s begins with the string t, ignoring case
-** * s is longer than t
-** * The first character of s beyond t is not a alphanumeric
-**
-** Ignore leading space in *s.
-**
-** To put it another way, return true if the first token of
-** s[] is t[].
-*/
-static int startsWith(const char *s, const char *t){
- while( safe_isspace(*s) ){ s++; }
- while( *t ){
- if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0;
- }
- return *s!='_' && !safe_isalnum(*s);
-}
-
-/*
-** An instance of this structure defines the "spec" of a
-** full text index. This structure is populated by parseSpec
-** and use by fulltextConnect and fulltextCreate.
-*/
-typedef struct TableSpec {
- const char *zDb; /* Logical database name */
- const char *zName; /* Name of the full-text index */
- int nColumn; /* Number of columns to be indexed */
- char **azColumn; /* Original names of columns to be indexed */
- char **azContentColumn; /* Column names for %_content */
- char **azTokenizer; /* Name of tokenizer and its arguments */
-} TableSpec;
-
-/*
-** Reclaim all of the memory used by a TableSpec
-*/
-static void clearTableSpec(TableSpec *p) {
- sqlite3_free(p->azColumn);
- sqlite3_free(p->azContentColumn);
- sqlite3_free(p->azTokenizer);
-}
-
-/* Parse a CREATE VIRTUAL TABLE statement, which looks like this:
- *
- * CREATE VIRTUAL TABLE email
- * USING fts2(subject, body, tokenize mytokenizer(myarg))
- *
- * We return parsed information in a TableSpec structure.
- *
- */
-static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv,
- char**pzErr){
- int i, n;
- char *z, *zDummy;
- char **azArg;
- const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */
-
- assert( argc>=3 );
- /* Current interface:
- ** argv[0] - module name
- ** argv[1] - database name
- ** argv[2] - table name
- ** argv[3..] - columns, optionally followed by tokenizer specification
- ** and snippet delimiters specification.
- */
-
- /* Make a copy of the complete argv[][] array in a single allocation.
- ** The argv[][] array is read-only and transient. We can write to the
- ** copy in order to modify things and the copy is persistent.
- */
- CLEAR(pSpec);
- for(i=n=0; izDb = azArg[1];
- pSpec->zName = azArg[2];
- pSpec->nColumn = 0;
- pSpec->azColumn = azArg;
- zTokenizer = "tokenize simple";
- for(i=3; inColumn] = firstToken(azArg[i], &zDummy);
- pSpec->nColumn++;
- }
- }
- if( pSpec->nColumn==0 ){
- azArg[0] = "content";
- pSpec->nColumn = 1;
- }
-
- /*
- ** Construct the list of content column names.
- **
- ** Each content column name will be of the form cNNAAAA
- ** where NN is the column number and AAAA is the sanitized
- ** column name. "sanitized" means that special characters are
- ** converted to "_". The cNN prefix guarantees that all column
- ** names are unique.
- **
- ** The AAAA suffix is not strictly necessary. It is included
- ** for the convenience of people who might examine the generated
- ** %_content table and wonder what the columns are used for.
- */
- pSpec->azContentColumn = sqlite3_malloc( pSpec->nColumn * sizeof(char *) );
- if( pSpec->azContentColumn==0 ){
- clearTableSpec(pSpec);
- return SQLITE_NOMEM;
- }
- for(i=0; inColumn; i++){
- char *p;
- pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]);
- for (p = pSpec->azContentColumn[i]; *p ; ++p) {
- if( !safe_isalnum(*p) ) *p = '_';
- }
- }
-
- /*
- ** Parse the tokenizer specification string.
- */
- pSpec->azTokenizer = tokenizeString(zTokenizer, &n);
- tokenListToIdList(pSpec->azTokenizer);
-
- return SQLITE_OK;
-}
-
-/*
-** Generate a CREATE TABLE statement that describes the schema of
-** the virtual table. Return a pointer to this schema string.
-**
-** Space is obtained from sqlite3_mprintf() and should be freed
-** using sqlite3_free().
-*/
-static char *fulltextSchema(
- int nColumn, /* Number of columns */
- const char *const* azColumn, /* List of columns */
- const char *zTableName /* Name of the table */
-){
- int i;
- char *zSchema, *zNext;
- const char *zSep = "(";
- zSchema = sqlite3_mprintf("CREATE TABLE x");
- for(i=0; ibase */
- v->db = db;
- v->zDb = spec->zDb; /* Freed when azColumn is freed */
- v->zName = spec->zName; /* Freed when azColumn is freed */
- v->nColumn = spec->nColumn;
- v->azContentColumn = spec->azContentColumn;
- spec->azContentColumn = 0;
- v->azColumn = spec->azColumn;
- spec->azColumn = 0;
-
- if( spec->azTokenizer==0 ){
- return SQLITE_NOMEM;
- }
-
- zTok = spec->azTokenizer[0];
- if( !zTok ){
- zTok = "simple";
- }
- nTok = strlen(zTok)+1;
-
- m = (sqlite3_tokenizer_module *)sqlite3Fts2HashFind(pHash, zTok, nTok);
- if( !m ){
- *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]);
- rc = SQLITE_ERROR;
- goto err;
- }
-
- for(n=0; spec->azTokenizer[n]; n++){}
- if( n ){
- rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1],
- &v->pTokenizer);
- }else{
- rc = m->xCreate(0, 0, &v->pTokenizer);
- }
- if( rc!=SQLITE_OK ) goto err;
- v->pTokenizer->pModule = m;
-
- /* TODO: verify the existence of backing tables foo_content, foo_term */
-
- schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn,
- spec->zName);
- rc = sqlite3_declare_vtab(db, schema);
- sqlite3_free(schema);
- if( rc!=SQLITE_OK ) goto err;
-
- memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements));
-
- /* Indicate that the buffer is not live. */
- v->nPendingData = -1;
-
- *ppVTab = &v->base;
- TRACE(("FTS2 Connect %p\n", v));
-
- return rc;
-
-err:
- fulltext_vtab_destroy(v);
- return rc;
-}
-
-static int fulltextConnect(
- sqlite3 *db,
- void *pAux,
- int argc, const char *const*argv,
- sqlite3_vtab **ppVTab,
- char **pzErr
-){
- TableSpec spec;
- int rc = parseSpec(&spec, argc, argv, pzErr);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = constructVtab(db, (fts2Hash *)pAux, &spec, ppVTab, pzErr);
- clearTableSpec(&spec);
- return rc;
-}
-
-/* The %_content table holds the text of each document, with
-** the rowid used as the docid.
-*/
-/* TODO(shess) This comment needs elaboration to match the updated
-** code. Work it into the top-of-file comment at that time.
-*/
-static int fulltextCreate(sqlite3 *db, void *pAux,
- int argc, const char * const *argv,
- sqlite3_vtab **ppVTab, char **pzErr){
- int rc;
- TableSpec spec;
- StringBuffer schema;
- TRACE(("FTS2 Create\n"));
-
- rc = parseSpec(&spec, argc, argv, pzErr);
- if( rc!=SQLITE_OK ) return rc;
-
- initStringBuffer(&schema);
- append(&schema, "CREATE TABLE %_content(");
- appendList(&schema, spec.nColumn, spec.azContentColumn);
- append(&schema, ")");
- rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema));
- stringBufferDestroy(&schema);
- if( rc!=SQLITE_OK ) goto out;
-
- rc = sql_exec(db, spec.zDb, spec.zName,
- "create table %_segments(block blob);");
- if( rc!=SQLITE_OK ) goto out;
-
- rc = sql_exec(db, spec.zDb, spec.zName,
- "create table %_segdir("
- " level integer,"
- " idx integer,"
- " start_block integer,"
- " leaves_end_block integer,"
- " end_block integer,"
- " root blob,"
- " primary key(level, idx)"
- ");");
- if( rc!=SQLITE_OK ) goto out;
-
- rc = constructVtab(db, (fts2Hash *)pAux, &spec, ppVTab, pzErr);
-
-out:
- clearTableSpec(&spec);
- return rc;
-}
-
-/* Decide how to handle an SQL query. */
-static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
- int i;
- TRACE(("FTS2 BestIndex\n"));
-
- for(i=0; inConstraint; ++i){
- const struct sqlite3_index_constraint *pConstraint;
- pConstraint = &pInfo->aConstraint[i];
- if( pConstraint->usable ) {
- if( pConstraint->iColumn==-1 &&
- pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
- pInfo->idxNum = QUERY_ROWID; /* lookup by rowid */
- TRACE(("FTS2 QUERY_ROWID\n"));
- } else if( pConstraint->iColumn>=0 &&
- pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
- /* full-text search */
- pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn;
- TRACE(("FTS2 QUERY_FULLTEXT %d\n", pConstraint->iColumn));
- } else continue;
-
- pInfo->aConstraintUsage[i].argvIndex = 1;
- pInfo->aConstraintUsage[i].omit = 1;
-
- /* An arbitrary value for now.
- * TODO: Perhaps rowid matches should be considered cheaper than
- * full-text searches. */
- pInfo->estimatedCost = 1.0;
-
- return SQLITE_OK;
- }
- }
- pInfo->idxNum = QUERY_GENERIC;
- return SQLITE_OK;
-}
-
-static int fulltextDisconnect(sqlite3_vtab *pVTab){
- TRACE(("FTS2 Disconnect %p\n", pVTab));
- fulltext_vtab_destroy((fulltext_vtab *)pVTab);
- return SQLITE_OK;
-}
-
-static int fulltextDestroy(sqlite3_vtab *pVTab){
- fulltext_vtab *v = (fulltext_vtab *)pVTab;
- int rc;
-
- TRACE(("FTS2 Destroy %p\n", pVTab));
- rc = sql_exec(v->db, v->zDb, v->zName,
- "drop table if exists %_content;"
- "drop table if exists %_segments;"
- "drop table if exists %_segdir;"
- );
- if( rc!=SQLITE_OK ) return rc;
-
- fulltext_vtab_destroy((fulltext_vtab *)pVTab);
- return SQLITE_OK;
-}
-
-static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
- fulltext_cursor *c;
-
- c = (fulltext_cursor *) sqlite3_malloc(sizeof(fulltext_cursor));
- if( c ){
- memset(c, 0, sizeof(fulltext_cursor));
- /* sqlite will initialize c->base */
- *ppCursor = &c->base;
- TRACE(("FTS2 Open %p: %p\n", pVTab, c));
- return SQLITE_OK;
- }else{
- return SQLITE_NOMEM;
- }
-}
-
-
-/* Free all of the dynamically allocated memory held by *q
-*/
-static void queryClear(Query *q){
- int i;
- for(i = 0; i < q->nTerms; ++i){
- sqlite3_free(q->pTerms[i].pTerm);
- }
- sqlite3_free(q->pTerms);
- CLEAR(q);
-}
-
-/* Free all of the dynamically allocated memory held by the
-** Snippet
-*/
-static void snippetClear(Snippet *p){
- sqlite3_free(p->aMatch);
- sqlite3_free(p->zOffset);
- sqlite3_free(p->zSnippet);
- CLEAR(p);
-}
-/*
-** Append a single entry to the p->aMatch[] log.
-*/
-static void snippetAppendMatch(
- Snippet *p, /* Append the entry to this snippet */
- int iCol, int iTerm, /* The column and query term */
- int iStart, int nByte /* Offset and size of the match */
-){
- int i;
- struct snippetMatch *pMatch;
- if( p->nMatch+1>=p->nAlloc ){
- p->nAlloc = p->nAlloc*2 + 10;
- p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
- if( p->aMatch==0 ){
- p->nMatch = 0;
- p->nAlloc = 0;
- return;
- }
- }
- i = p->nMatch++;
- pMatch = &p->aMatch[i];
- pMatch->iCol = iCol;
- pMatch->iTerm = iTerm;
- pMatch->iStart = iStart;
- pMatch->nByte = nByte;
-}
-
-/*
-** Sizing information for the circular buffer used in snippetOffsetsOfColumn()
-*/
-#define FTS2_ROTOR_SZ (32)
-#define FTS2_ROTOR_MASK (FTS2_ROTOR_SZ-1)
-
-/*
-** Add entries to pSnippet->aMatch[] for every match that occurs against
-** document zDoc[0..nDoc-1] which is stored in column iColumn.
-*/
-static void snippetOffsetsOfColumn(
- Query *pQuery,
- Snippet *pSnippet,
- int iColumn,
- const char *zDoc,
- int nDoc
-){
- const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */
- sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */
- sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */
- fulltext_vtab *pVtab; /* The full text index */
- int nColumn; /* Number of columns in the index */
- const QueryTerm *aTerm; /* Query string terms */
- int nTerm; /* Number of query string terms */
- int i, j; /* Loop counters */
- int rc; /* Return code */
- unsigned int match, prevMatch; /* Phrase search bitmasks */
- const char *zToken; /* Next token from the tokenizer */
- int nToken; /* Size of zToken */
- int iBegin, iEnd, iPos; /* Offsets of beginning and end */
-
- /* The following variables keep a circular buffer of the last
- ** few tokens */
- unsigned int iRotor = 0; /* Index of current token */
- int iRotorBegin[FTS2_ROTOR_SZ]; /* Beginning offset of token */
- int iRotorLen[FTS2_ROTOR_SZ]; /* Length of token */
-
- pVtab = pQuery->pFts;
- nColumn = pVtab->nColumn;
- pTokenizer = pVtab->pTokenizer;
- pTModule = pTokenizer->pModule;
- rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor);
- if( rc ) return;
- pTCursor->pTokenizer = pTokenizer;
- aTerm = pQuery->pTerms;
- nTerm = pQuery->nTerms;
- if( nTerm>=FTS2_ROTOR_SZ ){
- nTerm = FTS2_ROTOR_SZ - 1;
- }
- prevMatch = 0;
- while(1){
- rc = pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
- if( rc ) break;
- iRotorBegin[iRotor&FTS2_ROTOR_MASK] = iBegin;
- iRotorLen[iRotor&FTS2_ROTOR_MASK] = iEnd-iBegin;
- match = 0;
- for(i=0; i=0 && iColnToken ) continue;
- if( !aTerm[i].isPrefix && aTerm[i].nTerm1 && (prevMatch & (1<=0; j--){
- int k = (iRotor-j) & FTS2_ROTOR_MASK;
- snippetAppendMatch(pSnippet, iColumn, i-j,
- iRotorBegin[k], iRotorLen[k]);
- }
- }
- }
- prevMatch = match<<1;
- iRotor++;
- }
- pTModule->xClose(pTCursor);
-}
-
-
-/*
-** Compute all offsets for the current row of the query.
-** If the offsets have already been computed, this routine is a no-op.
-*/
-static void snippetAllOffsets(fulltext_cursor *p){
- int nColumn;
- int iColumn, i;
- int iFirst, iLast;
- fulltext_vtab *pFts;
-
- if( p->snippet.nMatch ) return;
- if( p->q.nTerms==0 ) return;
- pFts = p->q.pFts;
- nColumn = pFts->nColumn;
- iColumn = (p->iCursorType - QUERY_FULLTEXT);
- if( iColumn<0 || iColumn>=nColumn ){
- iFirst = 0;
- iLast = nColumn-1;
- }else{
- iFirst = iColumn;
- iLast = iColumn;
- }
- for(i=iFirst; i<=iLast; i++){
- const char *zDoc;
- int nDoc;
- zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1);
- nDoc = sqlite3_column_bytes(p->pStmt, i+1);
- snippetOffsetsOfColumn(&p->q, &p->snippet, i, zDoc, nDoc);
- }
-}
-
-/*
-** Convert the information in the aMatch[] array of the snippet
-** into the string zOffset[0..nOffset-1].
-*/
-static void snippetOffsetText(Snippet *p){
- int i;
- int cnt = 0;
- StringBuffer sb;
- char zBuf[200];
- if( p->zOffset ) return;
- initStringBuffer(&sb);
- for(i=0; inMatch; i++){
- struct snippetMatch *pMatch = &p->aMatch[i];
- zBuf[0] = ' ';
- sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
- pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
- append(&sb, zBuf);
- cnt++;
- }
- p->zOffset = stringBufferData(&sb);
- p->nOffset = stringBufferLength(&sb);
-}
-
-/*
-** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set
-** of matching words some of which might be in zDoc. zDoc is column
-** number iCol.
-**
-** iBreak is suggested spot in zDoc where we could begin or end an
-** excerpt. Return a value similar to iBreak but possibly adjusted
-** to be a little left or right so that the break point is better.
-*/
-static int wordBoundary(
- int iBreak, /* The suggested break point */
- const char *zDoc, /* Document text */
- int nDoc, /* Number of bytes in zDoc[] */
- struct snippetMatch *aMatch, /* Matching words */
- int nMatch, /* Number of entries in aMatch[] */
- int iCol /* The column number for zDoc[] */
-){
- int i;
- if( iBreak<=10 ){
- return 0;
- }
- if( iBreak>=nDoc-10 ){
- return nDoc;
- }
- for(i=0; i0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){
- return aMatch[i-1].iStart;
- }
- }
- for(i=1; i<=10; i++){
- if( safe_isspace(zDoc[iBreak-i]) ){
- return iBreak - i + 1;
- }
- if( safe_isspace(zDoc[iBreak+i]) ){
- return iBreak + i + 1;
- }
- }
- return iBreak;
-}
-
-
-
-/*
-** Allowed values for Snippet.aMatch[].snStatus
-*/
-#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */
-#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */
-
-/*
-** Generate the text of a snippet.
-*/
-static void snippetText(
- fulltext_cursor *pCursor, /* The cursor we need the snippet for */
- const char *zStartMark, /* Markup to appear before each match */
- const char *zEndMark, /* Markup to appear after each match */
- const char *zEllipsis /* Ellipsis mark */
-){
- int i, j;
- struct snippetMatch *aMatch;
- int nMatch;
- int nDesired;
- StringBuffer sb;
- int tailCol;
- int tailOffset;
- int iCol;
- int nDoc;
- const char *zDoc;
- int iStart, iEnd;
- int tailEllipsis = 0;
- int iMatch;
-
-
- sqlite3_free(pCursor->snippet.zSnippet);
- pCursor->snippet.zSnippet = 0;
- aMatch = pCursor->snippet.aMatch;
- nMatch = pCursor->snippet.nMatch;
- initStringBuffer(&sb);
-
- for(i=0; iq.nTerms; i++){
- for(j=0; j0; i++){
- if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue;
- nDesired--;
- iCol = aMatch[i].iCol;
- zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1);
- nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1);
- iStart = aMatch[i].iStart - 40;
- iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol);
- if( iStart<=10 ){
- iStart = 0;
- }
- if( iCol==tailCol && iStart<=tailOffset+20 ){
- iStart = tailOffset;
- }
- if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){
- trimWhiteSpace(&sb);
- appendWhiteSpace(&sb);
- append(&sb, zEllipsis);
- appendWhiteSpace(&sb);
- }
- iEnd = aMatch[i].iStart + aMatch[i].nByte + 40;
- iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol);
- if( iEnd>=nDoc-10 ){
- iEnd = nDoc;
- tailEllipsis = 0;
- }else{
- tailEllipsis = 1;
- }
- while( iMatchsnippet.zSnippet = stringBufferData(&sb);
- pCursor->snippet.nSnippet = stringBufferLength(&sb);
-}
-
-
-/*
-** Close the cursor. For additional information see the documentation
-** on the xClose method of the virtual table interface.
-*/
-static int fulltextClose(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- TRACE(("FTS2 Close %p\n", c));
- sqlite3_finalize(c->pStmt);
- queryClear(&c->q);
- snippetClear(&c->snippet);
- if( c->result.nData!=0 ) dlrDestroy(&c->reader);
- dataBufferDestroy(&c->result);
- sqlite3_free(c);
- return SQLITE_OK;
-}
-
-static int fulltextNext(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- int rc;
-
- TRACE(("FTS2 Next %p\n", pCursor));
- snippetClear(&c->snippet);
- if( c->iCursorType < QUERY_FULLTEXT ){
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
- rc = sqlite3_step(c->pStmt);
- switch( rc ){
- case SQLITE_ROW:
- c->eof = 0;
- return SQLITE_OK;
- case SQLITE_DONE:
- c->eof = 1;
- return SQLITE_OK;
- default:
- c->eof = 1;
- return rc;
- }
- } else { /* full-text query */
- rc = sqlite3_reset(c->pStmt);
- if( rc!=SQLITE_OK ) return rc;
-
- if( c->result.nData==0 || dlrAtEnd(&c->reader) ){
- c->eof = 1;
- return SQLITE_OK;
- }
- rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader));
- dlrStep(&c->reader);
- if( rc!=SQLITE_OK ) return rc;
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */
- rc = sqlite3_step(c->pStmt);
- if( rc==SQLITE_ROW ){ /* the case we expect */
- c->eof = 0;
- return SQLITE_OK;
- }
- /* an error occurred; abort */
- return rc==SQLITE_DONE ? SQLITE_ERROR : rc;
- }
-}
-
-
-/* TODO(shess) If we pushed LeafReader to the top of the file, or to
-** another file, term_select() could be pushed above
-** docListOfTerm().
-*/
-static int termSelect(fulltext_vtab *v, int iColumn,
- const char *pTerm, int nTerm, int isPrefix,
- DocListType iType, DataBuffer *out);
-
-/* Return a DocList corresponding to the query term *pTerm. If *pTerm
-** is the first term of a phrase query, go ahead and evaluate the phrase
-** query and return the doclist for the entire phrase query.
-**
-** The resulting DL_DOCIDS doclist is stored in pResult, which is
-** overwritten.
-*/
-static int docListOfTerm(
- fulltext_vtab *v, /* The full text index */
- int iColumn, /* column to restrict to. No restriction if >=nColumn */
- QueryTerm *pQTerm, /* Term we are looking for, or 1st term of a phrase */
- DataBuffer *pResult /* Write the result here */
-){
- DataBuffer left, right, new;
- int i, rc;
-
- /* No phrase search if no position info. */
- assert( pQTerm->nPhrase==0 || DL_DEFAULT!=DL_DOCIDS );
-
- /* This code should never be called with buffered updates. */
- assert( v->nPendingData<0 );
-
- dataBufferInit(&left, 0);
- rc = termSelect(v, iColumn, pQTerm->pTerm, pQTerm->nTerm, pQTerm->isPrefix,
- 0nPhrase ? DL_POSITIONS : DL_DOCIDS, &left);
- if( rc ) return rc;
- for(i=1; i<=pQTerm->nPhrase && left.nData>0; i++){
- dataBufferInit(&right, 0);
- rc = termSelect(v, iColumn, pQTerm[i].pTerm, pQTerm[i].nTerm,
- pQTerm[i].isPrefix, DL_POSITIONS, &right);
- if( rc ){
- dataBufferDestroy(&left);
- return rc;
- }
- dataBufferInit(&new, 0);
- docListPhraseMerge(left.pData, left.nData, right.pData, right.nData,
- inPhrase ? DL_POSITIONS : DL_DOCIDS, &new);
- dataBufferDestroy(&left);
- dataBufferDestroy(&right);
- left = new;
- }
- *pResult = left;
- return SQLITE_OK;
-}
-
-/* Add a new term pTerm[0..nTerm-1] to the query *q.
-*/
-static void queryAdd(Query *q, const char *pTerm, int nTerm){
- QueryTerm *t;
- ++q->nTerms;
- q->pTerms = sqlite3_realloc(q->pTerms, q->nTerms * sizeof(q->pTerms[0]));
- if( q->pTerms==0 ){
- q->nTerms = 0;
- return;
- }
- t = &q->pTerms[q->nTerms - 1];
- CLEAR(t);
- t->pTerm = sqlite3_malloc(nTerm+1);
- memcpy(t->pTerm, pTerm, nTerm);
- t->pTerm[nTerm] = 0;
- t->nTerm = nTerm;
- t->isOr = q->nextIsOr;
- t->isPrefix = 0;
- q->nextIsOr = 0;
- t->iColumn = q->nextColumn;
- q->nextColumn = q->dfltColumn;
-}
-
-/*
-** Check to see if the string zToken[0...nToken-1] matches any
-** column name in the virtual table. If it does,
-** return the zero-indexed column number. If not, return -1.
-*/
-static int checkColumnSpecifier(
- fulltext_vtab *pVtab, /* The virtual table */
- const char *zToken, /* Text of the token */
- int nToken /* Number of characters in the token */
-){
- int i;
- for(i=0; inColumn; i++){
- if( memcmp(pVtab->azColumn[i], zToken, nToken)==0
- && pVtab->azColumn[i][nToken]==0 ){
- return i;
- }
- }
- return -1;
-}
-
-/*
-** Parse the text at pSegment[0..nSegment-1]. Add additional terms
-** to the query being assemblied in pQuery.
-**
-** inPhrase is true if pSegment[0..nSegement-1] is contained within
-** double-quotes. If inPhrase is true, then the first term
-** is marked with the number of terms in the phrase less one and
-** OR and "-" syntax is ignored. If inPhrase is false, then every
-** term found is marked with nPhrase=0 and OR and "-" syntax is significant.
-*/
-static int tokenizeSegment(
- sqlite3_tokenizer *pTokenizer, /* The tokenizer to use */
- const char *pSegment, int nSegment, /* Query expression being parsed */
- int inPhrase, /* True if within "..." */
- Query *pQuery /* Append results here */
-){
- const sqlite3_tokenizer_module *pModule = pTokenizer->pModule;
- sqlite3_tokenizer_cursor *pCursor;
- int firstIndex = pQuery->nTerms;
- int iCol;
- int nTerm = 1;
-
- int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor);
- if( rc!=SQLITE_OK ) return rc;
- pCursor->pTokenizer = pTokenizer;
-
- while( 1 ){
- const char *pToken;
- int nToken, iBegin, iEnd, iPos;
-
- rc = pModule->xNext(pCursor,
- &pToken, &nToken,
- &iBegin, &iEnd, &iPos);
- if( rc!=SQLITE_OK ) break;
- if( !inPhrase &&
- pSegment[iEnd]==':' &&
- (iCol = checkColumnSpecifier(pQuery->pFts, pToken, nToken))>=0 ){
- pQuery->nextColumn = iCol;
- continue;
- }
- if( !inPhrase && pQuery->nTerms>0 && nToken==2
- && pSegment[iBegin]=='O' && pSegment[iBegin+1]=='R' ){
- pQuery->nextIsOr = 1;
- continue;
- }
- queryAdd(pQuery, pToken, nToken);
- if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){
- pQuery->pTerms[pQuery->nTerms-1].isNot = 1;
- }
- if( iEndpTerms[pQuery->nTerms-1].isPrefix = 1;
- }
- pQuery->pTerms[pQuery->nTerms-1].iPhrase = nTerm;
- if( inPhrase ){
- nTerm++;
- }
- }
-
- if( inPhrase && pQuery->nTerms>firstIndex ){
- pQuery->pTerms[firstIndex].nPhrase = pQuery->nTerms - firstIndex - 1;
- }
-
- return pModule->xClose(pCursor);
-}
-
-/* Parse a query string, yielding a Query object pQuery.
-**
-** The calling function will need to queryClear() to clean up
-** the dynamically allocated memory held by pQuery.
-*/
-static int parseQuery(
- fulltext_vtab *v, /* The fulltext index */
- const char *zInput, /* Input text of the query string */
- int nInput, /* Size of the input text */
- int dfltColumn, /* Default column of the index to match against */
- Query *pQuery /* Write the parse results here. */
-){
- int iInput, inPhrase = 0;
-
- if( zInput==0 ) nInput = 0;
- if( nInput<0 ) nInput = strlen(zInput);
- pQuery->nTerms = 0;
- pQuery->pTerms = NULL;
- pQuery->nextIsOr = 0;
- pQuery->nextColumn = dfltColumn;
- pQuery->dfltColumn = dfltColumn;
- pQuery->pFts = v;
-
- for(iInput=0; iInputiInput ){
- tokenizeSegment(v->pTokenizer, zInput+iInput, i-iInput, inPhrase,
- pQuery);
- }
- iInput = i;
- if( i=nColumn
-** they are allowed to match against any column.
-*/
-static int fulltextQuery(
- fulltext_vtab *v, /* The full text index */
- int iColumn, /* Match against this column by default */
- const char *zInput, /* The query string */
- int nInput, /* Number of bytes in zInput[] */
- DataBuffer *pResult, /* Write the result doclist here */
- Query *pQuery /* Put parsed query string here */
-){
- int i, iNext, rc;
- DataBuffer left, right, or, new;
- int nNot = 0;
- QueryTerm *aTerm;
-
- /* TODO(shess) Instead of flushing pendingTerms, we could query for
- ** the relevant term and merge the doclist into what we receive from
- ** the database. Wait and see if this is a common issue, first.
- **
- ** A good reason not to flush is to not generate update-related
- ** error codes from here.
- */
-
- /* Flush any buffered updates before executing the query. */
- rc = flushPendingTerms(v);
- if( rc!=SQLITE_OK ) return rc;
-
- /* TODO(shess) I think that the queryClear() calls below are not
- ** necessary, because fulltextClose() already clears the query.
- */
- rc = parseQuery(v, zInput, nInput, iColumn, pQuery);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Empty or NULL queries return no results. */
- if( pQuery->nTerms==0 ){
- dataBufferInit(pResult, 0);
- return SQLITE_OK;
- }
-
- /* Merge AND terms. */
- /* TODO(shess) I think we can early-exit if( i>nNot && left.nData==0 ). */
- aTerm = pQuery->pTerms;
- for(i = 0; inTerms; i=iNext){
- if( aTerm[i].isNot ){
- /* Handle all NOT terms in a separate pass */
- nNot++;
- iNext = i + aTerm[i].nPhrase+1;
- continue;
- }
- iNext = i + aTerm[i].nPhrase + 1;
- rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &right);
- if( rc ){
- if( i!=nNot ) dataBufferDestroy(&left);
- queryClear(pQuery);
- return rc;
- }
- while( iNextnTerms && aTerm[iNext].isOr ){
- rc = docListOfTerm(v, aTerm[iNext].iColumn, &aTerm[iNext], &or);
- iNext += aTerm[iNext].nPhrase + 1;
- if( rc ){
- if( i!=nNot ) dataBufferDestroy(&left);
- dataBufferDestroy(&right);
- queryClear(pQuery);
- return rc;
- }
- dataBufferInit(&new, 0);
- docListOrMerge(right.pData, right.nData, or.pData, or.nData, &new);
- dataBufferDestroy(&right);
- dataBufferDestroy(&or);
- right = new;
- }
- if( i==nNot ){ /* first term processed. */
- left = right;
- }else{
- dataBufferInit(&new, 0);
- docListAndMerge(left.pData, left.nData, right.pData, right.nData, &new);
- dataBufferDestroy(&right);
- dataBufferDestroy(&left);
- left = new;
- }
- }
-
- if( nNot==pQuery->nTerms ){
- /* We do not yet know how to handle a query of only NOT terms */
- return SQLITE_ERROR;
- }
-
- /* Do the EXCEPT terms */
- for(i=0; inTerms; i += aTerm[i].nPhrase + 1){
- if( !aTerm[i].isNot ) continue;
- rc = docListOfTerm(v, aTerm[i].iColumn, &aTerm[i], &right);
- if( rc ){
- queryClear(pQuery);
- dataBufferDestroy(&left);
- return rc;
- }
- dataBufferInit(&new, 0);
- docListExceptMerge(left.pData, left.nData, right.pData, right.nData, &new);
- dataBufferDestroy(&right);
- dataBufferDestroy(&left);
- left = new;
- }
-
- *pResult = left;
- return rc;
-}
-
-/*
-** This is the xFilter interface for the virtual table. See
-** the virtual table xFilter method documentation for additional
-** information.
-**
-** If idxNum==QUERY_GENERIC then do a full table scan against
-** the %_content table.
-**
-** If idxNum==QUERY_ROWID then do a rowid lookup for a single entry
-** in the %_content table.
-**
-** If idxNum>=QUERY_FULLTEXT then use the full text index. The
-** column on the left-hand side of the MATCH operator is column
-** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand
-** side of the MATCH operator.
-*/
-/* TODO(shess) Upgrade the cursor initialization and destruction to
-** account for fulltextFilter() being called multiple times on the
-** same cursor. The current solution is very fragile. Apply fix to
-** fts2 as appropriate.
-*/
-static int fulltextFilter(
- sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
- int idxNum, const char *idxStr, /* Which indexing scheme to use */
- int argc, sqlite3_value **argv /* Arguments for the indexing scheme */
-){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- fulltext_vtab *v = cursor_vtab(c);
- int rc;
-
- TRACE(("FTS2 Filter %p\n",pCursor));
-
- /* If the cursor has a statement that was not prepared according to
- ** idxNum, clear it. I believe all calls to fulltextFilter with a
- ** given cursor will have the same idxNum , but in this case it's
- ** easy to be safe.
- */
- if( c->pStmt && c->iCursorType!=idxNum ){
- sqlite3_finalize(c->pStmt);
- c->pStmt = NULL;
- }
-
- /* Get a fresh statement appropriate to idxNum. */
- /* TODO(shess): Add a prepared-statement cache in the vt structure.
- ** The cache must handle multiple open cursors. Easier to cache the
- ** statement variants at the vt to reduce malloc/realloc/free here.
- ** Or we could have a StringBuffer variant which allowed stack
- ** construction for small values.
- */
- if( !c->pStmt ){
- char *zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
- idxNum==QUERY_GENERIC ? "" : "where rowid=?");
- rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
- sqlite3_free(zSql);
- if( rc!=SQLITE_OK ) return rc;
- c->iCursorType = idxNum;
- }else{
- sqlite3_reset(c->pStmt);
- assert( c->iCursorType==idxNum );
- }
-
- switch( idxNum ){
- case QUERY_GENERIC:
- break;
-
- case QUERY_ROWID:
- rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0]));
- if( rc!=SQLITE_OK ) return rc;
- break;
-
- default: /* full-text search */
- {
- const char *zQuery = (const char *)sqlite3_value_text(argv[0]);
- assert( idxNum<=QUERY_FULLTEXT+v->nColumn);
- assert( argc==1 );
- queryClear(&c->q);
- if( c->result.nData!=0 ){
- /* This case happens if the same cursor is used repeatedly. */
- dlrDestroy(&c->reader);
- dataBufferReset(&c->result);
- }else{
- dataBufferInit(&c->result, 0);
- }
- rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &c->result, &c->q);
- if( rc!=SQLITE_OK ) return rc;
- if( c->result.nData!=0 ){
- dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData);
- }
- break;
- }
- }
-
- return fulltextNext(pCursor);
-}
-
-/* This is the xEof method of the virtual table. The SQLite core
-** calls this routine to find out if it has reached the end of
-** a query's results set.
-*/
-static int fulltextEof(sqlite3_vtab_cursor *pCursor){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- return c->eof;
-}
-
-/* This is the xColumn method of the virtual table. The SQLite
-** core calls this method during a query when it needs the value
-** of a column from the virtual table. This method needs to use
-** one of the sqlite3_result_*() routines to store the requested
-** value back in the pContext.
-*/
-static int fulltextColumn(sqlite3_vtab_cursor *pCursor,
- sqlite3_context *pContext, int idxCol){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
- fulltext_vtab *v = cursor_vtab(c);
-
- if( idxColnColumn ){
- sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
- sqlite3_result_value(pContext, pVal);
- }else if( idxCol==v->nColumn ){
- /* The extra column whose name is the same as the table.
- ** Return a blob which is a pointer to the cursor
- */
- sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
- }
- return SQLITE_OK;
-}
-
-/* This is the xRowid method. The SQLite core calls this routine to
-** retrive the rowid for the current row of the result set. The
-** rowid should be written to *pRowid.
-*/
-static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
- fulltext_cursor *c = (fulltext_cursor *) pCursor;
-
- *pRowid = sqlite3_column_int64(c->pStmt, 0);
- return SQLITE_OK;
-}
-
-/* Add all terms in [zText] to pendingTerms table. If [iColumn] > 0,
-** we also store positions and offsets in the hash table using that
-** column number.
-*/
-static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid,
- const char *zText, int iColumn){
- sqlite3_tokenizer *pTokenizer = v->pTokenizer;
- sqlite3_tokenizer_cursor *pCursor;
- const char *pToken;
- int nTokenBytes;
- int iStartOffset, iEndOffset, iPosition;
- int rc;
-
- rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor);
- if( rc!=SQLITE_OK ) return rc;
-
- pCursor->pTokenizer = pTokenizer;
- while( SQLITE_OK==(rc=pTokenizer->pModule->xNext(pCursor,
- &pToken, &nTokenBytes,
- &iStartOffset, &iEndOffset,
- &iPosition)) ){
- DLCollector *p;
- int nData; /* Size of doclist before our update. */
-
- /* Positions can't be negative; we use -1 as a terminator
- * internally. Token can't be NULL or empty. */
- if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){
- rc = SQLITE_ERROR;
- break;
- }
-
- p = fts2HashFind(&v->pendingTerms, pToken, nTokenBytes);
- if( p==NULL ){
- nData = 0;
- p = dlcNew(iDocid, DL_DEFAULT);
- fts2HashInsert(&v->pendingTerms, pToken, nTokenBytes, p);
-
- /* Overhead for our hash table entry, the key, and the value. */
- v->nPendingData += sizeof(struct fts2HashElem)+sizeof(*p)+nTokenBytes;
- }else{
- nData = p->b.nData;
- if( p->dlw.iPrevDocid!=iDocid ) dlcNext(p, iDocid);
- }
- if( iColumn>=0 ){
- dlcAddPos(p, iColumn, iPosition, iStartOffset, iEndOffset);
- }
-
- /* Accumulate data added by dlcNew or dlcNext, and dlcAddPos. */
- v->nPendingData += p->b.nData-nData;
- }
-
- /* TODO(shess) Check return? Should this be able to cause errors at
- ** this point? Actually, same question about sqlite3_finalize(),
- ** though one could argue that failure there means that the data is
- ** not durable. *ponder*
- */
- pTokenizer->pModule->xClose(pCursor);
- if( SQLITE_DONE == rc ) return SQLITE_OK;
- return rc;
-}
-
-/* Add doclists for all terms in [pValues] to pendingTerms table. */
-static int insertTerms(fulltext_vtab *v, sqlite_int64 iRowid,
- sqlite3_value **pValues){
- int i;
- for(i = 0; i < v->nColumn ; ++i){
- char *zText = (char*)sqlite3_value_text(pValues[i]);
- int rc = buildTerms(v, iRowid, zText, i);
- if( rc!=SQLITE_OK ) return rc;
- }
- return SQLITE_OK;
-}
-
-/* Add empty doclists for all terms in the given row's content to
-** pendingTerms.
-*/
-static int deleteTerms(fulltext_vtab *v, sqlite_int64 iRowid){
- const char **pValues;
- int i, rc;
-
- /* TODO(shess) Should we allow such tables at all? */
- if( DL_DEFAULT==DL_DOCIDS ) return SQLITE_ERROR;
-
- rc = content_select(v, iRowid, &pValues);
- if( rc!=SQLITE_OK ) return rc;
-
- for(i = 0 ; i < v->nColumn; ++i) {
- rc = buildTerms(v, iRowid, pValues[i], -1);
- if( rc!=SQLITE_OK ) break;
- }
-
- freeStringArray(v->nColumn, pValues);
- return SQLITE_OK;
-}
-
-/* TODO(shess) Refactor the code to remove this forward decl. */
-static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid);
-
-/* Insert a row into the %_content table; set *piRowid to be the ID of the
-** new row. Add doclists for terms to pendingTerms.
-*/
-static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestRowid,
- sqlite3_value **pValues, sqlite_int64 *piRowid){
- int rc;
-
- rc = content_insert(v, pRequestRowid, pValues); /* execute an SQL INSERT */
- if( rc!=SQLITE_OK ) return rc;
-
- *piRowid = sqlite3_last_insert_rowid(v->db);
- rc = initPendingTerms(v, *piRowid);
- if( rc!=SQLITE_OK ) return rc;
-
- return insertTerms(v, *piRowid, pValues);
-}
-
-/* Delete a row from the %_content table; add empty doclists for terms
-** to pendingTerms.
-*/
-static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){
- int rc = initPendingTerms(v, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = deleteTerms(v, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- return content_delete(v, iRow); /* execute an SQL DELETE */
-}
-
-/* Update a row in the %_content table; add delete doclists to
-** pendingTerms for old terms not in the new data, add insert doclists
-** to pendingTerms for terms in the new data.
-*/
-static int index_update(fulltext_vtab *v, sqlite_int64 iRow,
- sqlite3_value **pValues){
- int rc = initPendingTerms(v, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Generate an empty doclist for each term that previously appeared in this
- * row. */
- rc = deleteTerms(v, iRow);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */
- if( rc!=SQLITE_OK ) return rc;
-
- /* Now add positions for terms which appear in the updated row. */
- return insertTerms(v, iRow, pValues);
-}
-
-/*******************************************************************/
-/* InteriorWriter is used to collect terms and block references into
-** interior nodes in %_segments. See commentary at top of file for
-** format.
-*/
-
-/* How large interior nodes can grow. */
-#define INTERIOR_MAX 2048
-
-/* Minimum number of terms per interior node (except the root). This
-** prevents large terms from making the tree too skinny - must be >0
-** so that the tree always makes progress. Note that the min tree
-** fanout will be INTERIOR_MIN_TERMS+1.
-*/
-#define INTERIOR_MIN_TERMS 7
-#if INTERIOR_MIN_TERMS<1
-# error INTERIOR_MIN_TERMS must be greater than 0.
-#endif
-
-/* ROOT_MAX controls how much data is stored inline in the segment
-** directory.
-*/
-/* TODO(shess) Push ROOT_MAX down to whoever is writing things. It's
-** only here so that interiorWriterRootInfo() and leafWriterRootInfo()
-** can both see it, but if the caller passed it in, we wouldn't even
-** need a define.
-*/
-#define ROOT_MAX 1024
-#if ROOT_MAXterm, 0);
- dataBufferReplace(&block->term, pTerm, nTerm);
-
- n = putVarint(c, iHeight);
- n += putVarint(c+n, iChildBlock);
- dataBufferInit(&block->data, INTERIOR_MAX);
- dataBufferReplace(&block->data, c, n);
- }
- return block;
-}
-
-#ifndef NDEBUG
-/* Verify that the data is readable as an interior node. */
-static void interiorBlockValidate(InteriorBlock *pBlock){
- const char *pData = pBlock->data.pData;
- int nData = pBlock->data.nData;
- int n, iDummy;
- sqlite_int64 iBlockid;
-
- assert( nData>0 );
- assert( pData!=0 );
- assert( pData+nData>pData );
-
- /* Must lead with height of node as a varint(n), n>0 */
- n = getVarint32(pData, &iDummy);
- assert( n>0 );
- assert( iDummy>0 );
- assert( n0 );
- assert( n<=nData );
- pData += n;
- nData -= n;
-
- /* Zero or more terms of positive length */
- if( nData!=0 ){
- /* First term is not delta-encoded. */
- n = getVarint32(pData, &iDummy);
- assert( n>0 );
- assert( iDummy>0 );
- assert( n+iDummy>0);
- assert( n+iDummy<=nData );
- pData += n+iDummy;
- nData -= n+iDummy;
-
- /* Following terms delta-encoded. */
- while( nData!=0 ){
- /* Length of shared prefix. */
- n = getVarint32(pData, &iDummy);
- assert( n>0 );
- assert( iDummy>=0 );
- assert( n0 );
- assert( iDummy>0 );
- assert( n+iDummy>0);
- assert( n+iDummy<=nData );
- pData += n+iDummy;
- nData -= n+iDummy;
- }
- }
-}
-#define ASSERT_VALID_INTERIOR_BLOCK(x) interiorBlockValidate(x)
-#else
-#define ASSERT_VALID_INTERIOR_BLOCK(x) assert( 1 )
-#endif
-
-typedef struct InteriorWriter {
- int iHeight; /* from 0 at leaves. */
- InteriorBlock *first, *last;
- struct InteriorWriter *parentWriter;
-
- DataBuffer term; /* Last term written to block "last". */
- sqlite_int64 iOpeningChildBlock; /* First child block in block "last". */
-#ifndef NDEBUG
- sqlite_int64 iLastChildBlock; /* for consistency checks. */
-#endif
-} InteriorWriter;
-
-/* Initialize an interior node where pTerm[nTerm] marks the leftmost
-** term in the tree. iChildBlock is the leftmost child block at the
-** next level down the tree.
-*/
-static void interiorWriterInit(int iHeight, const char *pTerm, int nTerm,
- sqlite_int64 iChildBlock,
- InteriorWriter *pWriter){
- InteriorBlock *block;
- assert( iHeight>0 );
- CLEAR(pWriter);
-
- pWriter->iHeight = iHeight;
- pWriter->iOpeningChildBlock = iChildBlock;
-#ifndef NDEBUG
- pWriter->iLastChildBlock = iChildBlock;
-#endif
- block = interiorBlockNew(iHeight, iChildBlock, pTerm, nTerm);
- pWriter->last = pWriter->first = block;
- ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
- dataBufferInit(&pWriter->term, 0);
-}
-
-/* Append the child node rooted at iChildBlock to the interior node,
-** with pTerm[nTerm] as the leftmost term in iChildBlock's subtree.
-*/
-static void interiorWriterAppend(InteriorWriter *pWriter,
- const char *pTerm, int nTerm,
- sqlite_int64 iChildBlock){
- char c[VARINT_MAX+VARINT_MAX];
- int n, nPrefix = 0;
-
- ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
-
- /* The first term written into an interior node is actually
- ** associated with the second child added (the first child was added
- ** in interiorWriterInit, or in the if clause at the bottom of this
- ** function). That term gets encoded straight up, with nPrefix left
- ** at 0.
- */
- if( pWriter->term.nData==0 ){
- n = putVarint(c, nTerm);
- }else{
- while( nPrefixterm.nData &&
- pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){
- nPrefix++;
- }
-
- n = putVarint(c, nPrefix);
- n += putVarint(c+n, nTerm-nPrefix);
- }
-
-#ifndef NDEBUG
- pWriter->iLastChildBlock++;
-#endif
- assert( pWriter->iLastChildBlock==iChildBlock );
-
- /* Overflow to a new block if the new term makes the current block
- ** too big, and the current block already has enough terms.
- */
- if( pWriter->last->data.nData+n+nTerm-nPrefix>INTERIOR_MAX &&
- iChildBlock-pWriter->iOpeningChildBlock>INTERIOR_MIN_TERMS ){
- pWriter->last->next = interiorBlockNew(pWriter->iHeight, iChildBlock,
- pTerm, nTerm);
- pWriter->last = pWriter->last->next;
- pWriter->iOpeningChildBlock = iChildBlock;
- dataBufferReset(&pWriter->term);
- }else{
- dataBufferAppend2(&pWriter->last->data, c, n,
- pTerm+nPrefix, nTerm-nPrefix);
- dataBufferReplace(&pWriter->term, pTerm, nTerm);
- }
- ASSERT_VALID_INTERIOR_BLOCK(pWriter->last);
-}
-
-/* Free the space used by pWriter, including the linked-list of
-** InteriorBlocks, and parentWriter, if present.
-*/
-static int interiorWriterDestroy(InteriorWriter *pWriter){
- InteriorBlock *block = pWriter->first;
-
- while( block!=NULL ){
- InteriorBlock *b = block;
- block = block->next;
- dataBufferDestroy(&b->term);
- dataBufferDestroy(&b->data);
- sqlite3_free(b);
- }
- if( pWriter->parentWriter!=NULL ){
- interiorWriterDestroy(pWriter->parentWriter);
- sqlite3_free(pWriter->parentWriter);
- }
- dataBufferDestroy(&pWriter->term);
- SCRAMBLE(pWriter);
- return SQLITE_OK;
-}
-
-/* If pWriter can fit entirely in ROOT_MAX, return it as the root info
-** directly, leaving *piEndBlockid unchanged. Otherwise, flush
-** pWriter to %_segments, building a new layer of interior nodes, and
-** recursively ask for their root into.
-*/
-static int interiorWriterRootInfo(fulltext_vtab *v, InteriorWriter *pWriter,
- char **ppRootInfo, int *pnRootInfo,
- sqlite_int64 *piEndBlockid){
- InteriorBlock *block = pWriter->first;
- sqlite_int64 iBlockid = 0;
- int rc;
-
- /* If we can fit the segment inline */
- if( block==pWriter->last && block->data.nDatadata.pData;
- *pnRootInfo = block->data.nData;
- return SQLITE_OK;
- }
-
- /* Flush the first block to %_segments, and create a new level of
- ** interior node.
- */
- ASSERT_VALID_INTERIOR_BLOCK(block);
- rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid);
- if( rc!=SQLITE_OK ) return rc;
- *piEndBlockid = iBlockid;
-
- pWriter->parentWriter = sqlite3_malloc(sizeof(*pWriter->parentWriter));
- interiorWriterInit(pWriter->iHeight+1,
- block->term.pData, block->term.nData,
- iBlockid, pWriter->parentWriter);
-
- /* Flush additional blocks and append to the higher interior
- ** node.
- */
- for(block=block->next; block!=NULL; block=block->next){
- ASSERT_VALID_INTERIOR_BLOCK(block);
- rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid);
- if( rc!=SQLITE_OK ) return rc;
- *piEndBlockid = iBlockid;
-
- interiorWriterAppend(pWriter->parentWriter,
- block->term.pData, block->term.nData, iBlockid);
- }
-
- /* Parent node gets the chance to be the root. */
- return interiorWriterRootInfo(v, pWriter->parentWriter,
- ppRootInfo, pnRootInfo, piEndBlockid);
-}
-
-/****************************************************************/
-/* InteriorReader is used to read off the data from an interior node
-** (see comment at top of file for the format).
-*/
-typedef struct InteriorReader {
- const char *pData;
- int nData;
-
- DataBuffer term; /* previous term, for decoding term delta. */
-
- sqlite_int64 iBlockid;
-} InteriorReader;
-
-static void interiorReaderDestroy(InteriorReader *pReader){
- dataBufferDestroy(&pReader->term);
- SCRAMBLE(pReader);
-}
-
-/* TODO(shess) The assertions are great, but what if we're in NDEBUG
-** and the blob is empty or otherwise contains suspect data?
-*/
-static void interiorReaderInit(const char *pData, int nData,
- InteriorReader *pReader){
- int n, nTerm;
-
- /* Require at least the leading flag byte */
- assert( nData>0 );
- assert( pData[0]!='\0' );
-
- CLEAR(pReader);
-
- /* Decode the base blockid, and set the cursor to the first term. */
- n = getVarint(pData+1, &pReader->iBlockid);
- assert( 1+n<=nData );
- pReader->pData = pData+1+n;
- pReader->nData = nData-(1+n);
-
- /* A single-child interior node (such as when a leaf node was too
- ** large for the segment directory) won't have any terms.
- ** Otherwise, decode the first term.
- */
- if( pReader->nData==0 ){
- dataBufferInit(&pReader->term, 0);
- }else{
- n = getVarint32(pReader->pData, &nTerm);
- dataBufferInit(&pReader->term, nTerm);
- dataBufferReplace(&pReader->term, pReader->pData+n, nTerm);
- assert( n+nTerm<=pReader->nData );
- pReader->pData += n+nTerm;
- pReader->nData -= n+nTerm;
- }
-}
-
-static int interiorReaderAtEnd(InteriorReader *pReader){
- return pReader->term.nData==0;
-}
-
-static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){
- return pReader->iBlockid;
-}
-
-static int interiorReaderTermBytes(InteriorReader *pReader){
- assert( !interiorReaderAtEnd(pReader) );
- return pReader->term.nData;
-}
-static const char *interiorReaderTerm(InteriorReader *pReader){
- assert( !interiorReaderAtEnd(pReader) );
- return pReader->term.pData;
-}
-
-/* Step forward to the next term in the node. */
-static void interiorReaderStep(InteriorReader *pReader){
- assert( !interiorReaderAtEnd(pReader) );
-
- /* If the last term has been read, signal eof, else construct the
- ** next term.
- */
- if( pReader->nData==0 ){
- dataBufferReset(&pReader->term);
- }else{
- int n, nPrefix, nSuffix;
-
- n = getVarint32(pReader->pData, &nPrefix);
- n += getVarint32(pReader->pData+n, &nSuffix);
-
- /* Truncate the current term and append suffix data. */
- pReader->term.nData = nPrefix;
- dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix);
-
- assert( n+nSuffix<=pReader->nData );
- pReader->pData += n+nSuffix;
- pReader->nData -= n+nSuffix;
- }
- pReader->iBlockid++;
-}
-
-/* Compare the current term to pTerm[nTerm], returning strcmp-style
-** results. If isPrefix, equality means equal through nTerm bytes.
-*/
-static int interiorReaderTermCmp(InteriorReader *pReader,
- const char *pTerm, int nTerm, int isPrefix){
- const char *pReaderTerm = interiorReaderTerm(pReader);
- int nReaderTerm = interiorReaderTermBytes(pReader);
- int c, n = nReaderTerm0 ) return -1;
- if( nTerm>0 ) return 1;
- return 0;
- }
-
- c = memcmp(pReaderTerm, pTerm, n);
- if( c!=0 ) return c;
- if( isPrefix && n==nTerm ) return 0;
- return nReaderTerm - nTerm;
-}
-
-/****************************************************************/
-/* LeafWriter is used to collect terms and associated doclist data
-** into leaf blocks in %_segments (see top of file for format info).
-** Expected usage is:
-**
-** LeafWriter writer;
-** leafWriterInit(0, 0, &writer);
-** while( sorted_terms_left_to_process ){
-** // data is doclist data for that term.
-** rc = leafWriterStep(v, &writer, pTerm, nTerm, pData, nData);
-** if( rc!=SQLITE_OK ) goto err;
-** }
-** rc = leafWriterFinalize(v, &writer);
-**err:
-** leafWriterDestroy(&writer);
-** return rc;
-**
-** leafWriterStep() may write a collected leaf out to %_segments.
-** leafWriterFinalize() finishes writing any buffered data and stores
-** a root node in %_segdir. leafWriterDestroy() frees all buffers and
-** InteriorWriters allocated as part of writing this segment.
-**
-** TODO(shess) Document leafWriterStepMerge().
-*/
-
-/* Put terms with data this big in their own block. */
-#define STANDALONE_MIN 1024
-
-/* Keep leaf blocks below this size. */
-#define LEAF_MAX 2048
-
-typedef struct LeafWriter {
- int iLevel;
- int idx;
- sqlite_int64 iStartBlockid; /* needed to create the root info */
- sqlite_int64 iEndBlockid; /* when we're done writing. */
-
- DataBuffer term; /* previous encoded term */
- DataBuffer data; /* encoding buffer */
-
- /* bytes of first term in the current node which distinguishes that
- ** term from the last term of the previous node.
- */
- int nTermDistinct;
-
- InteriorWriter parentWriter; /* if we overflow */
- int has_parent;
-} LeafWriter;
-
-static void leafWriterInit(int iLevel, int idx, LeafWriter *pWriter){
- CLEAR(pWriter);
- pWriter->iLevel = iLevel;
- pWriter->idx = idx;
-
- dataBufferInit(&pWriter->term, 32);
-
- /* Start out with a reasonably sized block, though it can grow. */
- dataBufferInit(&pWriter->data, LEAF_MAX);
-}
-
-#ifndef NDEBUG
-/* Verify that the data is readable as a leaf node. */
-static void leafNodeValidate(const char *pData, int nData){
- int n, iDummy;
-
- if( nData==0 ) return;
- assert( nData>0 );
- assert( pData!=0 );
- assert( pData+nData>pData );
-
- /* Must lead with a varint(0) */
- n = getVarint32(pData, &iDummy);
- assert( iDummy==0 );
- assert( n>0 );
- assert( n0 );
- assert( iDummy>0 );
- assert( n+iDummy>0 );
- assert( n+iDummy0 );
- assert( iDummy>0 );
- assert( n+iDummy>0 );
- assert( n+iDummy<=nData );
- ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL);
- pData += n+iDummy;
- nData -= n+iDummy;
-
- /* Verify that trailing terms and doclists also are readable. */
- while( nData!=0 ){
- n = getVarint32(pData, &iDummy);
- assert( n>0 );
- assert( iDummy>=0 );
- assert( n0 );
- assert( iDummy>0 );
- assert( n+iDummy>0 );
- assert( n+iDummy0 );
- assert( iDummy>0 );
- assert( n+iDummy>0 );
- assert( n+iDummy<=nData );
- ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL);
- pData += n+iDummy;
- nData -= n+iDummy;
- }
-}
-#define ASSERT_VALID_LEAF_NODE(p, n) leafNodeValidate(p, n)
-#else
-#define ASSERT_VALID_LEAF_NODE(p, n) assert( 1 )
-#endif
-
-/* Flush the current leaf node to %_segments, and adding the resulting
-** blockid and the starting term to the interior node which will
-** contain it.
-*/
-static int leafWriterInternalFlush(fulltext_vtab *v, LeafWriter *pWriter,
- int iData, int nData){
- sqlite_int64 iBlockid = 0;
- const char *pStartingTerm;
- int nStartingTerm, rc, n;
-
- /* Must have the leading varint(0) flag, plus at least some
- ** valid-looking data.
- */
- assert( nData>2 );
- assert( iData>=0 );
- assert( iData+nData<=pWriter->data.nData );
- ASSERT_VALID_LEAF_NODE(pWriter->data.pData+iData, nData);
-
- rc = block_insert(v, pWriter->data.pData+iData, nData, &iBlockid);
- if( rc!=SQLITE_OK ) return rc;
- assert( iBlockid!=0 );
-
- /* Reconstruct the first term in the leaf for purposes of building
- ** the interior node.
- */
- n = getVarint32(pWriter->data.pData+iData+1, &nStartingTerm);
- pStartingTerm = pWriter->data.pData+iData+1+n;
- assert( pWriter->data.nData>iData+1+n+nStartingTerm );
- assert( pWriter->nTermDistinct>0 );
- assert( pWriter->nTermDistinct<=nStartingTerm );
- nStartingTerm = pWriter->nTermDistinct;
-
- if( pWriter->has_parent ){
- interiorWriterAppend(&pWriter->parentWriter,
- pStartingTerm, nStartingTerm, iBlockid);
- }else{
- interiorWriterInit(1, pStartingTerm, nStartingTerm, iBlockid,
- &pWriter->parentWriter);
- pWriter->has_parent = 1;
- }
-
- /* Track the span of this segment's leaf nodes. */
- if( pWriter->iEndBlockid==0 ){
- pWriter->iEndBlockid = pWriter->iStartBlockid = iBlockid;
- }else{
- pWriter->iEndBlockid++;
- assert( iBlockid==pWriter->iEndBlockid );
- }
-
- return SQLITE_OK;
-}
-static int leafWriterFlush(fulltext_vtab *v, LeafWriter *pWriter){
- int rc = leafWriterInternalFlush(v, pWriter, 0, pWriter->data.nData);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Re-initialize the output buffer. */
- dataBufferReset(&pWriter->data);
-
- return SQLITE_OK;
-}
-
-/* Fetch the root info for the segment. If the entire leaf fits
-** within ROOT_MAX, then it will be returned directly, otherwise it
-** will be flushed and the root info will be returned from the
-** interior node. *piEndBlockid is set to the blockid of the last
-** interior or leaf node written to disk (0 if none are written at
-** all).
-*/
-static int leafWriterRootInfo(fulltext_vtab *v, LeafWriter *pWriter,
- char **ppRootInfo, int *pnRootInfo,
- sqlite_int64 *piEndBlockid){
- /* we can fit the segment entirely inline */
- if( !pWriter->has_parent && pWriter->data.nDatadata.pData;
- *pnRootInfo = pWriter->data.nData;
- *piEndBlockid = 0;
- return SQLITE_OK;
- }
-
- /* Flush remaining leaf data. */
- if( pWriter->data.nData>0 ){
- int rc = leafWriterFlush(v, pWriter);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- /* We must have flushed a leaf at some point. */
- assert( pWriter->has_parent );
-
- /* Tenatively set the end leaf blockid as the end blockid. If the
- ** interior node can be returned inline, this will be the final
- ** blockid, otherwise it will be overwritten by
- ** interiorWriterRootInfo().
- */
- *piEndBlockid = pWriter->iEndBlockid;
-
- return interiorWriterRootInfo(v, &pWriter->parentWriter,
- ppRootInfo, pnRootInfo, piEndBlockid);
-}
-
-/* Collect the rootInfo data and store it into the segment directory.
-** This has the effect of flushing the segment's leaf data to
-** %_segments, and also flushing any interior nodes to %_segments.
-*/
-static int leafWriterFinalize(fulltext_vtab *v, LeafWriter *pWriter){
- sqlite_int64 iEndBlockid;
- char *pRootInfo;
- int rc, nRootInfo;
-
- rc = leafWriterRootInfo(v, pWriter, &pRootInfo, &nRootInfo, &iEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Don't bother storing an entirely empty segment. */
- if( iEndBlockid==0 && nRootInfo==0 ) return SQLITE_OK;
-
- return segdir_set(v, pWriter->iLevel, pWriter->idx,
- pWriter->iStartBlockid, pWriter->iEndBlockid,
- iEndBlockid, pRootInfo, nRootInfo);
-}
-
-static void leafWriterDestroy(LeafWriter *pWriter){
- if( pWriter->has_parent ) interiorWriterDestroy(&pWriter->parentWriter);
- dataBufferDestroy(&pWriter->term);
- dataBufferDestroy(&pWriter->data);
-}
-
-/* Encode a term into the leafWriter, delta-encoding as appropriate.
-** Returns the length of the new term which distinguishes it from the
-** previous term, which can be used to set nTermDistinct when a node
-** boundary is crossed.
-*/
-static int leafWriterEncodeTerm(LeafWriter *pWriter,
- const char *pTerm, int nTerm){
- char c[VARINT_MAX+VARINT_MAX];
- int n, nPrefix = 0;
-
- assert( nTerm>0 );
- while( nPrefixterm.nData &&
- pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){
- nPrefix++;
- /* Failing this implies that the terms weren't in order. */
- assert( nPrefixdata.nData==0 ){
- /* Encode the node header and leading term as:
- ** varint(0)
- ** varint(nTerm)
- ** char pTerm[nTerm]
- */
- n = putVarint(c, '\0');
- n += putVarint(c+n, nTerm);
- dataBufferAppend2(&pWriter->data, c, n, pTerm, nTerm);
- }else{
- /* Delta-encode the term as:
- ** varint(nPrefix)
- ** varint(nSuffix)
- ** char pTermSuffix[nSuffix]
- */
- n = putVarint(c, nPrefix);
- n += putVarint(c+n, nTerm-nPrefix);
- dataBufferAppend2(&pWriter->data, c, n, pTerm+nPrefix, nTerm-nPrefix);
- }
- dataBufferReplace(&pWriter->term, pTerm, nTerm);
-
- return nPrefix+1;
-}
-
-/* Used to avoid a memmove when a large amount of doclist data is in
-** the buffer. This constructs a node and term header before
-** iDoclistData and flushes the resulting complete node using
-** leafWriterInternalFlush().
-*/
-static int leafWriterInlineFlush(fulltext_vtab *v, LeafWriter *pWriter,
- const char *pTerm, int nTerm,
- int iDoclistData){
- char c[VARINT_MAX+VARINT_MAX];
- int iData, n = putVarint(c, 0);
- n += putVarint(c+n, nTerm);
-
- /* There should always be room for the header. Even if pTerm shared
- ** a substantial prefix with the previous term, the entire prefix
- ** could be constructed from earlier data in the doclist, so there
- ** should be room.
- */
- assert( iDoclistData>=n+nTerm );
-
- iData = iDoclistData-(n+nTerm);
- memcpy(pWriter->data.pData+iData, c, n);
- memcpy(pWriter->data.pData+iData+n, pTerm, nTerm);
-
- return leafWriterInternalFlush(v, pWriter, iData, pWriter->data.nData-iData);
-}
-
-/* Push pTerm[nTerm] along with the doclist data to the leaf layer of
-** %_segments.
-*/
-static int leafWriterStepMerge(fulltext_vtab *v, LeafWriter *pWriter,
- const char *pTerm, int nTerm,
- DLReader *pReaders, int nReaders){
- char c[VARINT_MAX+VARINT_MAX];
- int iTermData = pWriter->data.nData, iDoclistData;
- int i, nData, n, nActualData, nActual, rc, nTermDistinct;
-
- ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData);
- nTermDistinct = leafWriterEncodeTerm(pWriter, pTerm, nTerm);
-
- /* Remember nTermDistinct if opening a new node. */
- if( iTermData==0 ) pWriter->nTermDistinct = nTermDistinct;
-
- iDoclistData = pWriter->data.nData;
-
- /* Estimate the length of the merged doclist so we can leave space
- ** to encode it.
- */
- for(i=0, nData=0; idata, c, n);
-
- docListMerge(&pWriter->data, pReaders, nReaders);
- ASSERT_VALID_DOCLIST(DL_DEFAULT,
- pWriter->data.pData+iDoclistData+n,
- pWriter->data.nData-iDoclistData-n, NULL);
-
- /* The actual amount of doclist data at this point could be smaller
- ** than the length we encoded. Additionally, the space required to
- ** encode this length could be smaller. For small doclists, this is
- ** not a big deal, we can just use memmove() to adjust things.
- */
- nActualData = pWriter->data.nData-(iDoclistData+n);
- nActual = putVarint(c, nActualData);
- assert( nActualData<=nData );
- assert( nActual<=n );
-
- /* If the new doclist is big enough for force a standalone leaf
- ** node, we can immediately flush it inline without doing the
- ** memmove().
- */
- /* TODO(shess) This test matches leafWriterStep(), which does this
- ** test before it knows the cost to varint-encode the term and
- ** doclist lengths. At some point, change to
- ** pWriter->data.nData-iTermData>STANDALONE_MIN.
- */
- if( nTerm+nActualData>STANDALONE_MIN ){
- /* Push leaf node from before this term. */
- if( iTermData>0 ){
- rc = leafWriterInternalFlush(v, pWriter, 0, iTermData);
- if( rc!=SQLITE_OK ) return rc;
-
- pWriter->nTermDistinct = nTermDistinct;
- }
-
- /* Fix the encoded doclist length. */
- iDoclistData += n - nActual;
- memcpy(pWriter->data.pData+iDoclistData, c, nActual);
-
- /* Push the standalone leaf node. */
- rc = leafWriterInlineFlush(v, pWriter, pTerm, nTerm, iDoclistData);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Leave the node empty. */
- dataBufferReset(&pWriter->data);
-
- return rc;
- }
-
- /* At this point, we know that the doclist was small, so do the
- ** memmove if indicated.
- */
- if( nActualdata.pData+iDoclistData+nActual,
- pWriter->data.pData+iDoclistData+n,
- pWriter->data.nData-(iDoclistData+n));
- pWriter->data.nData -= n-nActual;
- }
-
- /* Replace written length with actual length. */
- memcpy(pWriter->data.pData+iDoclistData, c, nActual);
-
- /* If the node is too large, break things up. */
- /* TODO(shess) This test matches leafWriterStep(), which does this
- ** test before it knows the cost to varint-encode the term and
- ** doclist lengths. At some point, change to
- ** pWriter->data.nData>LEAF_MAX.
- */
- if( iTermData+nTerm+nActualData>LEAF_MAX ){
- /* Flush out the leading data as a node */
- rc = leafWriterInternalFlush(v, pWriter, 0, iTermData);
- if( rc!=SQLITE_OK ) return rc;
-
- pWriter->nTermDistinct = nTermDistinct;
-
- /* Rebuild header using the current term */
- n = putVarint(pWriter->data.pData, 0);
- n += putVarint(pWriter->data.pData+n, nTerm);
- memcpy(pWriter->data.pData+n, pTerm, nTerm);
- n += nTerm;
-
- /* There should always be room, because the previous encoding
- ** included all data necessary to construct the term.
- */
- assert( ndata.nData-iDoclistDatadata.pData+n,
- pWriter->data.pData+iDoclistData,
- pWriter->data.nData-iDoclistData);
- pWriter->data.nData -= iDoclistData-n;
- }
- ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData);
-
- return SQLITE_OK;
-}
-
-/* Push pTerm[nTerm] along with the doclist data to the leaf layer of
-** %_segments.
-*/
-/* TODO(shess) Revise writeZeroSegment() so that doclists are
-** constructed directly in pWriter->data.
-*/
-static int leafWriterStep(fulltext_vtab *v, LeafWriter *pWriter,
- const char *pTerm, int nTerm,
- const char *pData, int nData){
- int rc;
- DLReader reader;
-
- dlrInit(&reader, DL_DEFAULT, pData, nData);
- rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1);
- dlrDestroy(&reader);
-
- return rc;
-}
-
-
-/****************************************************************/
-/* LeafReader is used to iterate over an individual leaf node. */
-typedef struct LeafReader {
- DataBuffer term; /* copy of current term. */
-
- const char *pData; /* data for current term. */
- int nData;
-} LeafReader;
-
-static void leafReaderDestroy(LeafReader *pReader){
- dataBufferDestroy(&pReader->term);
- SCRAMBLE(pReader);
-}
-
-static int leafReaderAtEnd(LeafReader *pReader){
- return pReader->nData<=0;
-}
-
-/* Access the current term. */
-static int leafReaderTermBytes(LeafReader *pReader){
- return pReader->term.nData;
-}
-static const char *leafReaderTerm(LeafReader *pReader){
- assert( pReader->term.nData>0 );
- return pReader->term.pData;
-}
-
-/* Access the doclist data for the current term. */
-static int leafReaderDataBytes(LeafReader *pReader){
- int nData;
- assert( pReader->term.nData>0 );
- getVarint32(pReader->pData, &nData);
- return nData;
-}
-static const char *leafReaderData(LeafReader *pReader){
- int n, nData;
- assert( pReader->term.nData>0 );
- n = getVarint32(pReader->pData, &nData);
- return pReader->pData+n;
-}
-
-static void leafReaderInit(const char *pData, int nData,
- LeafReader *pReader){
- int nTerm, n;
-
- assert( nData>0 );
- assert( pData[0]=='\0' );
-
- CLEAR(pReader);
-
- /* Read the first term, skipping the header byte. */
- n = getVarint32(pData+1, &nTerm);
- dataBufferInit(&pReader->term, nTerm);
- dataBufferReplace(&pReader->term, pData+1+n, nTerm);
-
- /* Position after the first term. */
- assert( 1+n+nTermpData = pData+1+n+nTerm;
- pReader->nData = nData-1-n-nTerm;
-}
-
-/* Step the reader forward to the next term. */
-static void leafReaderStep(LeafReader *pReader){
- int n, nData, nPrefix, nSuffix;
- assert( !leafReaderAtEnd(pReader) );
-
- /* Skip previous entry's data block. */
- n = getVarint32(pReader->pData, &nData);
- assert( n+nData<=pReader->nData );
- pReader->pData += n+nData;
- pReader->nData -= n+nData;
-
- if( !leafReaderAtEnd(pReader) ){
- /* Construct the new term using a prefix from the old term plus a
- ** suffix from the leaf data.
- */
- n = getVarint32(pReader->pData, &nPrefix);
- n += getVarint32(pReader->pData+n, &nSuffix);
- assert( n+nSuffixnData );
- pReader->term.nData = nPrefix;
- dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix);
-
- pReader->pData += n+nSuffix;
- pReader->nData -= n+nSuffix;
- }
-}
-
-/* strcmp-style comparison of pReader's current term against pTerm.
-** If isPrefix, equality means equal through nTerm bytes.
-*/
-static int leafReaderTermCmp(LeafReader *pReader,
- const char *pTerm, int nTerm, int isPrefix){
- int c, n = pReader->term.nDataterm.nData : nTerm;
- if( n==0 ){
- if( pReader->term.nData>0 ) return -1;
- if(nTerm>0 ) return 1;
- return 0;
- }
-
- c = memcmp(pReader->term.pData, pTerm, n);
- if( c!=0 ) return c;
- if( isPrefix && n==nTerm ) return 0;
- return pReader->term.nData - nTerm;
-}
-
-
-/****************************************************************/
-/* LeavesReader wraps LeafReader to allow iterating over the entire
-** leaf layer of the tree.
-*/
-typedef struct LeavesReader {
- int idx; /* Index within the segment. */
-
- sqlite3_stmt *pStmt; /* Statement we're streaming leaves from. */
- int eof; /* we've seen SQLITE_DONE from pStmt. */
-
- LeafReader leafReader; /* reader for the current leaf. */
- DataBuffer rootData; /* root data for inline. */
-} LeavesReader;
-
-/* Access the current term. */
-static int leavesReaderTermBytes(LeavesReader *pReader){
- assert( !pReader->eof );
- return leafReaderTermBytes(&pReader->leafReader);
-}
-static const char *leavesReaderTerm(LeavesReader *pReader){
- assert( !pReader->eof );
- return leafReaderTerm(&pReader->leafReader);
-}
-
-/* Access the doclist data for the current term. */
-static int leavesReaderDataBytes(LeavesReader *pReader){
- assert( !pReader->eof );
- return leafReaderDataBytes(&pReader->leafReader);
-}
-static const char *leavesReaderData(LeavesReader *pReader){
- assert( !pReader->eof );
- return leafReaderData(&pReader->leafReader);
-}
-
-static int leavesReaderAtEnd(LeavesReader *pReader){
- return pReader->eof;
-}
-
-/* loadSegmentLeaves() may not read all the way to SQLITE_DONE, thus
-** leaving the statement handle open, which locks the table.
-*/
-/* TODO(shess) This "solution" is not satisfactory. Really, there
-** should be check-in function for all statement handles which
-** arranges to call sqlite3_reset(). This most likely will require
-** modification to control flow all over the place, though, so for now
-** just punt.
-**
-** Note the current system assumes that segment merges will run to
-** completion, which is why this particular probably hasn't arisen in
-** this case. Probably a brittle assumption.
-*/
-static int leavesReaderReset(LeavesReader *pReader){
- return sqlite3_reset(pReader->pStmt);
-}
-
-static void leavesReaderDestroy(LeavesReader *pReader){
- /* If idx is -1, that means we're using a non-cached statement
- ** handle in the optimize() case, so we need to release it.
- */
- if( pReader->pStmt!=NULL && pReader->idx==-1 ){
- sqlite3_finalize(pReader->pStmt);
- }
- leafReaderDestroy(&pReader->leafReader);
- dataBufferDestroy(&pReader->rootData);
- SCRAMBLE(pReader);
-}
-
-/* Initialize pReader with the given root data (if iStartBlockid==0
-** the leaf data was entirely contained in the root), or from the
-** stream of blocks between iStartBlockid and iEndBlockid, inclusive.
-*/
-static int leavesReaderInit(fulltext_vtab *v,
- int idx,
- sqlite_int64 iStartBlockid,
- sqlite_int64 iEndBlockid,
- const char *pRootData, int nRootData,
- LeavesReader *pReader){
- CLEAR(pReader);
- pReader->idx = idx;
-
- dataBufferInit(&pReader->rootData, 0);
- if( iStartBlockid==0 ){
- /* Entire leaf level fit in root data. */
- dataBufferReplace(&pReader->rootData, pRootData, nRootData);
- leafReaderInit(pReader->rootData.pData, pReader->rootData.nData,
- &pReader->leafReader);
- }else{
- sqlite3_stmt *s;
- int rc = sql_get_leaf_statement(v, idx, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iStartBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 2, iEndBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ){
- pReader->eof = 1;
- return SQLITE_OK;
- }
- if( rc!=SQLITE_ROW ) return rc;
-
- pReader->pStmt = s;
- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
- sqlite3_column_bytes(pReader->pStmt, 0),
- &pReader->leafReader);
- }
- return SQLITE_OK;
-}
-
-/* Step the current leaf forward to the next term. If we reach the
-** end of the current leaf, step forward to the next leaf block.
-*/
-static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){
- assert( !leavesReaderAtEnd(pReader) );
- leafReaderStep(&pReader->leafReader);
-
- if( leafReaderAtEnd(&pReader->leafReader) ){
- int rc;
- if( pReader->rootData.pData ){
- pReader->eof = 1;
- return SQLITE_OK;
- }
- rc = sqlite3_step(pReader->pStmt);
- if( rc!=SQLITE_ROW ){
- pReader->eof = 1;
- return rc==SQLITE_DONE ? SQLITE_OK : rc;
- }
- leafReaderDestroy(&pReader->leafReader);
- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
- sqlite3_column_bytes(pReader->pStmt, 0),
- &pReader->leafReader);
- }
- return SQLITE_OK;
-}
-
-/* Order LeavesReaders by their term, ignoring idx. Readers at eof
-** always sort to the end.
-*/
-static int leavesReaderTermCmp(LeavesReader *lr1, LeavesReader *lr2){
- if( leavesReaderAtEnd(lr1) ){
- if( leavesReaderAtEnd(lr2) ) return 0;
- return 1;
- }
- if( leavesReaderAtEnd(lr2) ) return -1;
-
- return leafReaderTermCmp(&lr1->leafReader,
- leavesReaderTerm(lr2), leavesReaderTermBytes(lr2),
- 0);
-}
-
-/* Similar to leavesReaderTermCmp(), with additional ordering by idx
-** so that older segments sort before newer segments.
-*/
-static int leavesReaderCmp(LeavesReader *lr1, LeavesReader *lr2){
- int c = leavesReaderTermCmp(lr1, lr2);
- if( c!=0 ) return c;
- return lr1->idx-lr2->idx;
-}
-
-/* Assume that pLr[1]..pLr[nLr] are sorted. Bubble pLr[0] into its
-** sorted position.
-*/
-static void leavesReaderReorder(LeavesReader *pLr, int nLr){
- while( nLr>1 && leavesReaderCmp(pLr, pLr+1)>0 ){
- LeavesReader tmp = pLr[0];
- pLr[0] = pLr[1];
- pLr[1] = tmp;
- nLr--;
- pLr++;
- }
-}
-
-/* Initializes pReaders with the segments from level iLevel, returning
-** the number of segments in *piReaders. Leaves pReaders in sorted
-** order.
-*/
-static int leavesReadersInit(fulltext_vtab *v, int iLevel,
- LeavesReader *pReaders, int *piReaders){
- sqlite3_stmt *s;
- int i, rc = sql_get_statement(v, SEGDIR_SELECT_LEVEL_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int(s, 1, iLevel);
- if( rc!=SQLITE_OK ) return rc;
-
- i = 0;
- while( (rc = sqlite3_step(s))==SQLITE_ROW ){
- sqlite_int64 iStart = sqlite3_column_int64(s, 0);
- sqlite_int64 iEnd = sqlite3_column_int64(s, 1);
- const char *pRootData = sqlite3_column_blob(s, 2);
- int nRootData = sqlite3_column_bytes(s, 2);
-
- assert( i0 ){
- leavesReaderDestroy(&pReaders[i]);
- }
- return rc;
- }
-
- *piReaders = i;
-
- /* Leave our results sorted by term, then age. */
- while( i-- ){
- leavesReaderReorder(pReaders+i, *piReaders-i);
- }
- return SQLITE_OK;
-}
-
-/* Merge doclists from pReaders[nReaders] into a single doclist, which
-** is written to pWriter. Assumes pReaders is ordered oldest to
-** newest.
-*/
-/* TODO(shess) Consider putting this inline in segmentMerge(). */
-static int leavesReadersMerge(fulltext_vtab *v,
- LeavesReader *pReaders, int nReaders,
- LeafWriter *pWriter){
- DLReader dlReaders[MERGE_COUNT];
- const char *pTerm = leavesReaderTerm(pReaders);
- int i, nTerm = leavesReaderTermBytes(pReaders);
-
- assert( nReaders<=MERGE_COUNT );
-
- for(i=0; i0 ){
- rc = leavesReaderStep(v, lrs+i);
- if( rc!=SQLITE_OK ) goto err;
-
- /* Reorder by term, then by age. */
- leavesReaderReorder(lrs+i, MERGE_COUNT-i);
- }
- }
-
- for(i=0; i0 );
-
- for(rc=SQLITE_OK; rc==SQLITE_OK && !leavesReaderAtEnd(pReader);
- rc=leavesReaderStep(v, pReader)){
- /* TODO(shess) Really want leavesReaderTermCmp(), but that name is
- ** already taken to compare the terms of two LeavesReaders. Think
- ** on a better name. [Meanwhile, break encapsulation rather than
- ** use a confusing name.]
- */
- int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix);
- if( c>0 ) break; /* Past any possible matches. */
- if( c==0 ){
- const char *pData = leavesReaderData(pReader);
- int iBuffer, nData = leavesReaderDataBytes(pReader);
-
- /* Find the first empty buffer. */
- for(iBuffer=0; iBuffer0 ){
- assert(pBuffers!=NULL);
- memcpy(p, pBuffers, nBuffers*sizeof(*pBuffers));
- sqlite3_free(pBuffers);
- }
- pBuffers = p;
- }
- dataBufferInit(&(pBuffers[nBuffers]), 0);
- nBuffers++;
- }
-
- /* At this point, must have an empty at iBuffer. */
- assert(iBufferpData, p->nData);
-
- /* dataBufferReset() could allow a large doclist to blow up
- ** our memory requirements.
- */
- if( p->nCapacity<1024 ){
- dataBufferReset(p);
- }else{
- dataBufferDestroy(p);
- dataBufferInit(p, 0);
- }
- }
- }
- }
- }
-
- /* Union all the doclists together into *out. */
- /* TODO(shess) What if *out is big? Sigh. */
- if( rc==SQLITE_OK && nBuffers>0 ){
- int iBuffer;
- for(iBuffer=0; iBuffer0 ){
- if( out->nData==0 ){
- dataBufferSwap(out, &(pBuffers[iBuffer]));
- }else{
- docListAccumulateUnion(out, pBuffers[iBuffer].pData,
- pBuffers[iBuffer].nData);
- }
- }
- }
- }
-
- while( nBuffers-- ){
- dataBufferDestroy(&(pBuffers[nBuffers]));
- }
- if( pBuffers!=NULL ) sqlite3_free(pBuffers);
-
- return rc;
-}
-
-/* Call loadSegmentLeavesInt() with pData/nData as input. */
-static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData,
- const char *pTerm, int nTerm, int isPrefix,
- DataBuffer *out){
- LeavesReader reader;
- int rc;
-
- assert( nData>1 );
- assert( *pData=='\0' );
- rc = leavesReaderInit(v, 0, 0, 0, pData, nData, &reader);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out);
- leavesReaderReset(&reader);
- leavesReaderDestroy(&reader);
- return rc;
-}
-
-/* Call loadSegmentLeavesInt() with the leaf nodes from iStartLeaf to
-** iEndLeaf (inclusive) as input, and merge the resulting doclist into
-** out.
-*/
-static int loadSegmentLeaves(fulltext_vtab *v,
- sqlite_int64 iStartLeaf, sqlite_int64 iEndLeaf,
- const char *pTerm, int nTerm, int isPrefix,
- DataBuffer *out){
- int rc;
- LeavesReader reader;
-
- assert( iStartLeaf<=iEndLeaf );
- rc = leavesReaderInit(v, 0, iStartLeaf, iEndLeaf, NULL, 0, &reader);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out);
- leavesReaderReset(&reader);
- leavesReaderDestroy(&reader);
- return rc;
-}
-
-/* Taking pData/nData as an interior node, find the sequence of child
-** nodes which could include pTerm/nTerm/isPrefix. Note that the
-** interior node terms logically come between the blocks, so there is
-** one more blockid than there are terms (that block contains terms >=
-** the last interior-node term).
-*/
-/* TODO(shess) The calling code may already know that the end child is
-** not worth calculating, because the end may be in a later sibling
-** node. Consider whether breaking symmetry is worthwhile. I suspect
-** it is not worthwhile.
-*/
-static void getChildrenContaining(const char *pData, int nData,
- const char *pTerm, int nTerm, int isPrefix,
- sqlite_int64 *piStartChild,
- sqlite_int64 *piEndChild){
- InteriorReader reader;
-
- assert( nData>1 );
- assert( *pData!='\0' );
- interiorReaderInit(pData, nData, &reader);
-
- /* Scan for the first child which could contain pTerm/nTerm. */
- while( !interiorReaderAtEnd(&reader) ){
- if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break;
- interiorReaderStep(&reader);
- }
- *piStartChild = interiorReaderCurrentBlockid(&reader);
-
- /* Keep scanning to find a term greater than our term, using prefix
- ** comparison if indicated. If isPrefix is false, this will be the
- ** same blockid as the starting block.
- */
- while( !interiorReaderAtEnd(&reader) ){
- if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break;
- interiorReaderStep(&reader);
- }
- *piEndChild = interiorReaderCurrentBlockid(&reader);
-
- interiorReaderDestroy(&reader);
-
- /* Children must ascend, and if !prefix, both must be the same. */
- assert( *piEndChild>=*piStartChild );
- assert( isPrefix || *piStartChild==*piEndChild );
-}
-
-/* Read block at iBlockid and pass it with other params to
-** getChildrenContaining().
-*/
-static int loadAndGetChildrenContaining(
- fulltext_vtab *v,
- sqlite_int64 iBlockid,
- const char *pTerm, int nTerm, int isPrefix,
- sqlite_int64 *piStartChild, sqlite_int64 *piEndChild
-){
- sqlite3_stmt *s = NULL;
- int rc;
-
- assert( iBlockid!=0 );
- assert( pTerm!=NULL );
- assert( nTerm!=0 ); /* TODO(shess) Why not allow this? */
- assert( piStartChild!=NULL );
- assert( piEndChild!=NULL );
-
- rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_bind_int64(s, 1, iBlockid);
- if( rc!=SQLITE_OK ) return rc;
-
- rc = sqlite3_step(s);
- if( rc==SQLITE_DONE ) return SQLITE_ERROR;
- if( rc!=SQLITE_ROW ) return rc;
-
- getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0),
- pTerm, nTerm, isPrefix, piStartChild, piEndChild);
-
- /* We expect only one row. We must execute another sqlite3_step()
- * to complete the iteration; otherwise the table will remain
- * locked. */
- rc = sqlite3_step(s);
- if( rc==SQLITE_ROW ) return SQLITE_ERROR;
- if( rc!=SQLITE_DONE ) return rc;
-
- return SQLITE_OK;
-}
-
-/* Traverse the tree represented by pData[nData] looking for
-** pTerm[nTerm], placing its doclist into *out. This is internal to
-** loadSegment() to make error-handling cleaner.
-*/
-static int loadSegmentInt(fulltext_vtab *v, const char *pData, int nData,
- sqlite_int64 iLeavesEnd,
- const char *pTerm, int nTerm, int isPrefix,
- DataBuffer *out){
- /* Special case where root is a leaf. */
- if( *pData=='\0' ){
- return loadSegmentLeaf(v, pData, nData, pTerm, nTerm, isPrefix, out);
- }else{
- int rc;
- sqlite_int64 iStartChild, iEndChild;
-
- /* Process pData as an interior node, then loop down the tree
- ** until we find the set of leaf nodes to scan for the term.
- */
- getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix,
- &iStartChild, &iEndChild);
- while( iStartChild>iLeavesEnd ){
- sqlite_int64 iNextStart, iNextEnd;
- rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix,
- &iNextStart, &iNextEnd);
- if( rc!=SQLITE_OK ) return rc;
-
- /* If we've branched, follow the end branch, too. */
- if( iStartChild!=iEndChild ){
- sqlite_int64 iDummy;
- rc = loadAndGetChildrenContaining(v, iEndChild, pTerm, nTerm, isPrefix,
- &iDummy, &iNextEnd);
- if( rc!=SQLITE_OK ) return rc;
- }
-
- assert( iNextStart<=iNextEnd );
- iStartChild = iNextStart;
- iEndChild = iNextEnd;
- }
- assert( iStartChild<=iLeavesEnd );
- assert( iEndChild<=iLeavesEnd );
-
- /* Scan through the leaf segments for doclists. */
- return loadSegmentLeaves(v, iStartChild, iEndChild,
- pTerm, nTerm, isPrefix, out);
- }
-}
-
-/* Call loadSegmentInt() to collect the doclist for pTerm/nTerm, then
-** merge its doclist over *out (any duplicate doclists read from the
-** segment rooted at pData will overwrite those in *out).
-*/
-/* TODO(shess) Consider changing this to determine the depth of the
-** leaves using either the first characters of interior nodes (when
-** ==1, we're one level above the leaves), or the first character of
-** the root (which will describe the height of the tree directly).
-** Either feels somewhat tricky to me.
-*/
-/* TODO(shess) The current merge is likely to be slow for large
-** doclists (though it should process from newest/smallest to
-** oldest/largest, so it may not be that bad). It might be useful to
-** modify things to allow for N-way merging. This could either be
-** within a segment, with pairwise merges across segments, or across
-** all segments at once.
-*/
-static int loadSegment(fulltext_vtab *v, const char *pData, int nData,
- sqlite_int64 iLeavesEnd,
- const char *pTerm, int nTerm, int isPrefix,
- DataBuffer *out){
- DataBuffer result;
- int rc;
-
- assert( nData>1 );
-
- /* This code should never be called with buffered updates. */
- assert( v->nPendingData<0 );
-
- dataBufferInit(&result, 0);
- rc = loadSegmentInt(v, pData, nData, iLeavesEnd,
- pTerm, nTerm, isPrefix, &result);
- if( rc==SQLITE_OK && result.nData>0 ){
- if( out->nData==0 ){
- DataBuffer tmp = *out;
- *out = result;
- result = tmp;
- }else{
- DataBuffer merged;
- DLReader readers[2];
-
- dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData);
- dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData);
- dataBufferInit(&merged, out->nData+result.nData);
- docListMerge(&merged, readers, 2);
- dataBufferDestroy(out);
- *out = merged;
- dlrDestroy(&readers[0]);
- dlrDestroy(&readers[1]);
- }
- }
- dataBufferDestroy(&result);
- return rc;
-}
-
-/* Scan the database and merge together the posting lists for the term
-** into *out.
-*/
-static int termSelect(fulltext_vtab *v, int iColumn,
- const char *pTerm, int nTerm, int isPrefix,
- DocListType iType, DataBuffer *out){
- DataBuffer doclist;
- sqlite3_stmt *s;
- int rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
- if( rc!=SQLITE_OK ) return rc;
-
- /* This code should never be called with buffered updates. */
- assert( v->nPendingData<0 );
-
- dataBufferInit(&doclist, 0);
-
- /* Traverse the segments from oldest to newest so that newer doclist
- ** elements for given docids overwrite older elements.
- */
- while( (rc = sqlite3_step(s))==SQLITE_ROW ){
- const char *pData = sqlite3_column_blob(s, 2);
- const int nData = sqlite3_column_bytes(s, 2);
- const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
- rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix,
- &doclist);
- if( rc!=SQLITE_OK ) goto err;
- }
- if( rc==SQLITE_DONE ){
- if( doclist.nData!=0 ){
- /* TODO(shess) The old term_select_all() code applied the column
- ** restrict as we merged segments, leading to smaller buffers.
- ** This is probably worthwhile to bring back, once the new storage
- ** system is checked in.
- */
- if( iColumn==v->nColumn) iColumn = -1;
- docListTrim(DL_DEFAULT, doclist.pData, doclist.nData,
- iColumn, iType, out);
- }
- rc = SQLITE_OK;
- }
-
- err:
- dataBufferDestroy(&doclist);
- return rc;
-}
-
-/****************************************************************/
-/* Used to hold hashtable data for sorting. */
-typedef struct TermData {
- const char *pTerm;
- int nTerm;
- DLCollector *pCollector;
-} TermData;
-
-/* Orders TermData elements in strcmp fashion ( <0 for less-than, 0
-** for equal, >0 for greater-than).
-*/
-static int termDataCmp(const void *av, const void *bv){
- const TermData *a = (const TermData *)av;
- const TermData *b = (const TermData *)bv;
- int n = a->nTermnTerm ? a->nTerm : b->nTerm;
- int c = memcmp(a->pTerm, b->pTerm, n);
- if( c!=0 ) return c;
- return a->nTerm-b->nTerm;
-}
-
-/* Order pTerms data by term, then write a new level 0 segment using
-** LeafWriter.
-*/
-static int writeZeroSegment(fulltext_vtab *v, fts2Hash *pTerms){
- fts2HashElem *e;
- int idx, rc, i, n;
- TermData *pData;
- LeafWriter writer;
- DataBuffer dl;
-
- /* Determine the next index at level 0, merging as necessary. */
- rc = segdirNextIndex(v, 0, &idx);
- if( rc!=SQLITE_OK ) return rc;
-
- n = fts2HashCount(pTerms);
- pData = sqlite3_malloc(n*sizeof(TermData));
-
- for(i = 0, e = fts2HashFirst(pTerms); e; i++, e = fts2HashNext(e)){
- assert( i1 ) qsort(pData, n, sizeof(*pData), termDataCmp);
-
- /* TODO(shess) Refactor so that we can write directly to the segment
- ** DataBuffer, as happens for segment merges.
- */
- leafWriterInit(0, idx, &writer);
- dataBufferInit(&dl, 0);
- for(i=0; inPendingData>=0 ){
- fts2HashElem *e;
- for(e=fts2HashFirst(&v->pendingTerms); e; e=fts2HashNext(e)){
- dlcDelete(fts2HashData(e));
- }
- fts2HashClear(&v->pendingTerms);
- v->nPendingData = -1;
- }
- return SQLITE_OK;
-}
-
-/* If pendingTerms has data, flush it to a level-zero segment, and
-** free it.
-*/
-static int flushPendingTerms(fulltext_vtab *v){
- if( v->nPendingData>=0 ){
- int rc = writeZeroSegment(v, &v->pendingTerms);
- if( rc==SQLITE_OK ) clearPendingTerms(v);
- return rc;
- }
- return SQLITE_OK;
-}
-
-/* If pendingTerms is "too big", or docid is out of order, flush it.
-** Regardless, be certain that pendingTerms is initialized for use.
-*/
-static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid){
- /* TODO(shess) Explore whether partially flushing the buffer on
- ** forced-flush would provide better performance. I suspect that if
- ** we ordered the doclists by size and flushed the largest until the
- ** buffer was half empty, that would let the less frequent terms
- ** generate longer doclists.
- */
- if( iDocid<=v->iPrevDocid || v->nPendingData>kPendingThreshold ){
- int rc = flushPendingTerms(v);
- if( rc!=SQLITE_OK ) return rc;
- }
- if( v->nPendingData<0 ){
- fts2HashInit(&v->pendingTerms, FTS2_HASH_STRING, 1);
- v->nPendingData = 0;
- }
- v->iPrevDocid = iDocid;
- return SQLITE_OK;
-}
-
-/* This function implements the xUpdate callback; it is the top-level entry
- * point for inserting, deleting or updating a row in a full-text table. */
-static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg,
- sqlite_int64 *pRowid){
- fulltext_vtab *v = (fulltext_vtab *) pVtab;
- int rc;
-
- TRACE(("FTS2 Update %p\n", pVtab));
-
- if( nArg<2 ){
- rc = index_delete(v, sqlite3_value_int64(ppArg[0]));
- if( rc==SQLITE_OK ){
- /* If we just deleted the last row in the table, clear out the
- ** index data.
- */
- rc = content_exists(v);
- if( rc==SQLITE_ROW ){
- rc = SQLITE_OK;
- }else if( rc==SQLITE_DONE ){
- /* Clear the pending terms so we don't flush a useless level-0
- ** segment when the transaction closes.
- */
- rc = clearPendingTerms(v);
- if( rc==SQLITE_OK ){
- rc = segdir_delete_all(v);
- }
- }
- }
- } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){
- /* An update:
- * ppArg[0] = old rowid
- * ppArg[1] = new rowid
- * ppArg[2..2+v->nColumn-1] = values
- * ppArg[2+v->nColumn] = value for magic column (we ignore this)
- */
- sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]);
- if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER ||
- sqlite3_value_int64(ppArg[1]) != rowid ){
- rc = SQLITE_ERROR; /* we don't allow changing the rowid */
- } else {
- assert( nArg==2+v->nColumn+1);
- rc = index_update(v, rowid, &ppArg[2]);
- }
- } else {
- /* An insert:
- * ppArg[1] = requested rowid
- * ppArg[2..2+v->nColumn-1] = values
- * ppArg[2+v->nColumn] = value for magic column (we ignore this)
- */
- assert( nArg==2+v->nColumn+1);
- rc = index_insert(v, ppArg[1], &ppArg[2], pRowid);
- }
-
- return rc;
-}
-
-static int fulltextSync(sqlite3_vtab *pVtab){
- TRACE(("FTS2 xSync()\n"));
- return flushPendingTerms((fulltext_vtab *)pVtab);
-}
-
-static int fulltextBegin(sqlite3_vtab *pVtab){
- fulltext_vtab *v = (fulltext_vtab *) pVtab;
- TRACE(("FTS2 xBegin()\n"));
-
- /* Any buffered updates should have been cleared by the previous
- ** transaction.
- */
- assert( v->nPendingData<0 );
- return clearPendingTerms(v);
-}
-
-static int fulltextCommit(sqlite3_vtab *pVtab){
- fulltext_vtab *v = (fulltext_vtab *) pVtab;
- TRACE(("FTS2 xCommit()\n"));
-
- /* Buffered updates should have been cleared by fulltextSync(). */
- assert( v->nPendingData<0 );
- return clearPendingTerms(v);
-}
-
-static int fulltextRollback(sqlite3_vtab *pVtab){
- TRACE(("FTS2 xRollback()\n"));
- return clearPendingTerms((fulltext_vtab *)pVtab);
-}
-
-/*
-** Implementation of the snippet() function for FTS2
-*/
-static void snippetFunc(
- sqlite3_context *pContext,
- int argc,
- sqlite3_value **argv
-){
- fulltext_cursor *pCursor;
- if( argc<1 ) return;
- if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
- sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
- sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1);
- }else{
- const char *zStart = "";
- const char *zEnd = "";
- const char *zEllipsis = "...";
- memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
- if( argc>=2 ){
- zStart = (const char*)sqlite3_value_text(argv[1]);
- if( argc>=3 ){
- zEnd = (const char*)sqlite3_value_text(argv[2]);
- if( argc>=4 ){
- zEllipsis = (const char*)sqlite3_value_text(argv[3]);
- }
- }
- }
- snippetAllOffsets(pCursor);
- snippetText(pCursor, zStart, zEnd, zEllipsis);
- sqlite3_result_text(pContext, pCursor->snippet.zSnippet,
- pCursor->snippet.nSnippet, SQLITE_STATIC);
- }
-}
-
-/*
-** Implementation of the offsets() function for FTS2
-*/
-static void snippetOffsetsFunc(
- sqlite3_context *pContext,
- int argc,
- sqlite3_value **argv
-){
- fulltext_cursor *pCursor;
- if( argc<1 ) return;
- if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
- sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
- sqlite3_result_error(pContext, "illegal first argument to offsets",-1);
- }else{
- memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
- snippetAllOffsets(pCursor);
- snippetOffsetText(&pCursor->snippet);
- sqlite3_result_text(pContext,
- pCursor->snippet.zOffset, pCursor->snippet.nOffset,
- SQLITE_STATIC);
- }
-}
-
-/* OptLeavesReader is nearly identical to LeavesReader, except that
-** where LeavesReader is geared towards the merging of complete
-** segment levels (with exactly MERGE_COUNT segments), OptLeavesReader
-** is geared towards implementation of the optimize() function, and
-** can merge all segments simultaneously. This version may be
-** somewhat less efficient than LeavesReader because it merges into an
-** accumulator rather than doing an N-way merge, but since segment
-** size grows exponentially (so segment count logrithmically) this is
-** probably not an immediate problem.
-*/
-/* TODO(shess): Prove that assertion, or extend the merge code to
-** merge tree fashion (like the prefix-searching code does).
-*/
-/* TODO(shess): OptLeavesReader and LeavesReader could probably be
-** merged with little or no loss of performance for LeavesReader. The
-** merged code would need to handle >MERGE_COUNT segments, and would
-** also need to be able to optionally optimize away deletes.
-*/
-typedef struct OptLeavesReader {
- /* Segment number, to order readers by age. */
- int segment;
- LeavesReader reader;
-} OptLeavesReader;
-
-static int optLeavesReaderAtEnd(OptLeavesReader *pReader){
- return leavesReaderAtEnd(&pReader->reader);
-}
-static int optLeavesReaderTermBytes(OptLeavesReader *pReader){
- return leavesReaderTermBytes(&pReader->reader);
-}
-static const char *optLeavesReaderData(OptLeavesReader *pReader){
- return leavesReaderData(&pReader->reader);
-}
-static int optLeavesReaderDataBytes(OptLeavesReader *pReader){
- return leavesReaderDataBytes(&pReader->reader);
-}
-static const char *optLeavesReaderTerm(OptLeavesReader *pReader){
- return leavesReaderTerm(&pReader->reader);
-}
-static int optLeavesReaderStep(fulltext_vtab *v, OptLeavesReader *pReader){
- return leavesReaderStep(v, &pReader->reader);
-}
-static int optLeavesReaderTermCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){
- return leavesReaderTermCmp(&lr1->reader, &lr2->reader);
-}
-/* Order by term ascending, segment ascending (oldest to newest), with
-** exhausted readers to the end.
-*/
-static int optLeavesReaderCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){
- int c = optLeavesReaderTermCmp(lr1, lr2);
- if( c!=0 ) return c;
- return lr1->segment-lr2->segment;
-}
-/* Bubble pLr[0] to appropriate place in pLr[1..nLr-1]. Assumes that
-** pLr[1..nLr-1] is already sorted.
-*/
-static void optLeavesReaderReorder(OptLeavesReader *pLr, int nLr){
- while( nLr>1 && optLeavesReaderCmp(pLr, pLr+1)>0 ){
- OptLeavesReader tmp = pLr[0];
- pLr[0] = pLr[1];
- pLr[1] = tmp;
- nLr--;
- pLr++;
- }
-}
-
-/* optimize() helper function. Put the readers in order and iterate
-** through them, merging doclists for matching terms into pWriter.
-** Returns SQLITE_OK on success, or the SQLite error code which
-** prevented success.
-*/
-static int optimizeInternal(fulltext_vtab *v,
- OptLeavesReader *readers, int nReaders,
- LeafWriter *pWriter){
- int i, rc = SQLITE_OK;
- DataBuffer doclist, merged, tmp;
-
- /* Order the readers. */
- i = nReaders;
- while( i-- > 0 ){
- optLeavesReaderReorder(&readers[i], nReaders-i);
- }
-
- dataBufferInit(&doclist, LEAF_MAX);
- dataBufferInit(&merged, LEAF_MAX);
-
- /* Exhausted readers bubble to the end, so when the first reader is
- ** at eof, all are at eof.
- */
- while( !optLeavesReaderAtEnd(&readers[0]) ){
-
- /* Figure out how many readers share the next term. */
- for(i=1; i 0 ){
- dlrDestroy(&dlReaders[nReaders]);
- }
-
- /* Accumulated doclist to reader 0 for next pass. */
- dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData);
- }
-
- /* Destroy reader that was left in the pipeline. */
- dlrDestroy(&dlReaders[0]);
-
- /* Trim deletions from the doclist. */
- dataBufferReset(&merged);
- docListTrim(DL_DEFAULT, doclist.pData, doclist.nData,
- -1, DL_DEFAULT, &merged);
- }
-
- /* Only pass doclists with hits (skip if all hits deleted). */
- if( merged.nData>0 ){
- rc = leafWriterStep(v, pWriter,
- optLeavesReaderTerm(&readers[0]),
- optLeavesReaderTermBytes(&readers[0]),
- merged.pData, merged.nData);
- if( rc!=SQLITE_OK ) goto err;
- }
-
- /* Step merged readers to next term and reorder. */
- while( i-- > 0 ){
- rc = optLeavesReaderStep(v, &readers[i]);
- if( rc!=SQLITE_OK ) goto err;
-
- optLeavesReaderReorder(&readers[i], nReaders-i);
- }
- }
-
- err:
- dataBufferDestroy(&doclist);
- dataBufferDestroy(&merged);
- return rc;
-}
-
-/* Implement optimize() function for FTS3. optimize(t) merges all
-** segments in the fts index into a single segment. 't' is the magic
-** table-named column.
-*/
-static void optimizeFunc(sqlite3_context *pContext,
- int argc, sqlite3_value **argv){
- fulltext_cursor *pCursor;
- if( argc>1 ){
- sqlite3_result_error(pContext, "excess arguments to optimize()",-1);
- }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
- sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
- sqlite3_result_error(pContext, "illegal first argument to optimize",-1);
- }else{
- fulltext_vtab *v;
- int i, rc, iMaxLevel;
- OptLeavesReader *readers;
- int nReaders;
- LeafWriter writer;
- sqlite3_stmt *s;
-
- memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
- v = cursor_vtab(pCursor);
-
- /* Flush any buffered updates before optimizing. */
- rc = flushPendingTerms(v);
- if( rc!=SQLITE_OK ) goto err;
-
- rc = segdir_count(v, &nReaders, &iMaxLevel);
- if( rc!=SQLITE_OK ) goto err;
- if( nReaders==0 || nReaders==1 ){
- sqlite3_result_text(pContext, "Index already optimal", -1,
- SQLITE_STATIC);
- return;
- }
-
- rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s);
- if( rc!=SQLITE_OK ) goto err;
-
- readers = sqlite3_malloc(nReaders*sizeof(readers[0]));
- if( readers==NULL ) goto err;
-
- /* Note that there will already be a segment at this position
- ** until we call segdir_delete() on iMaxLevel.
- */
- leafWriterInit(iMaxLevel, 0, &writer);
-
- i = 0;
- while( (rc = sqlite3_step(s))==SQLITE_ROW ){
- sqlite_int64 iStart = sqlite3_column_int64(s, 0);
- sqlite_int64 iEnd = sqlite3_column_int64(s, 1);
- const char *pRootData = sqlite3_column_blob(s, 2);
- int nRootData = sqlite3_column_bytes(s, 2);
-
- assert( i 0 ){
- leavesReaderDestroy(&readers[i].reader);
- }
- sqlite3_free(readers);
-
- /* If we've successfully gotten to here, delete the old segments
- ** and flush the interior structure of the new segment.
- */
- if( rc==SQLITE_OK ){
- for( i=0; i<=iMaxLevel; i++ ){
- rc = segdir_delete(v, i);
- if( rc!=SQLITE_OK ) break;
- }
-
- if( rc==SQLITE_OK ) rc = leafWriterFinalize(v, &writer);
- }
-
- leafWriterDestroy(&writer);
-
- if( rc!=SQLITE_OK ) goto err;
-
- sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC);
- return;
-
- /* TODO(shess): Error-handling needs to be improved along the
- ** lines of the dump_ functions.
- */
- err:
- {
- char buf[512];
- sqlite3_snprintf(sizeof(buf), buf, "Error in optimize: %s",
- sqlite3_errmsg(sqlite3_context_db_handle(pContext)));
- sqlite3_result_error(pContext, buf, -1);
- }
- }
-}
-
-#ifdef SQLITE_TEST
-/* Generate an error of the form ": ". If msg is NULL,
-** pull the error from the context's db handle.
-*/
-static void generateError(sqlite3_context *pContext,
- const char *prefix, const char *msg){
- char buf[512];
- if( msg==NULL ) msg = sqlite3_errmsg(sqlite3_context_db_handle(pContext));
- sqlite3_snprintf(sizeof(buf), buf, "%s: %s", prefix, msg);
- sqlite3_result_error(pContext, buf, -1);
-}
-
-/* Helper function to collect the set of terms in the segment into
-** pTerms. The segment is defined by the leaf nodes between
-** iStartBlockid and iEndBlockid, inclusive, or by the contents of
-** pRootData if iStartBlockid is 0 (in which case the entire segment
-** fit in a leaf).
-*/
-static int collectSegmentTerms(fulltext_vtab *v, sqlite3_stmt *s,
- fts2Hash *pTerms){
- const sqlite_int64 iStartBlockid = sqlite3_column_int64(s, 0);
- const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1);
- const char *pRootData = sqlite3_column_blob(s, 2);
- const int nRootData = sqlite3_column_bytes(s, 2);
- LeavesReader reader;
- int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid,
- pRootData, nRootData, &reader);
- if( rc!=SQLITE_OK ) return rc;
-
- while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){
- const char *pTerm = leavesReaderTerm(&reader);
- const int nTerm = leavesReaderTermBytes(&reader);
- void *oldValue = sqlite3Fts2HashFind(pTerms, pTerm, nTerm);
- void *newValue = (void *)((char *)oldValue+1);
-
- /* From the comment before sqlite3Fts2HashInsert in fts2_hash.c,
- ** the data value passed is returned in case of malloc failure.
- */
- if( newValue==sqlite3Fts2HashInsert(pTerms, pTerm, nTerm, newValue) ){
- rc = SQLITE_NOMEM;
- }else{
- rc = leavesReaderStep(v, &reader);
- }
- }
-
- leavesReaderDestroy(&reader);
- return rc;
-}
-
-/* Helper function to build the result string for dump_terms(). */
-static int generateTermsResult(sqlite3_context *pContext, fts2Hash *pTerms){
- int iTerm, nTerms, nResultBytes, iByte;
- char *result;
- TermData *pData;
- fts2HashElem *e;
-
- /* Iterate pTerms to generate an array of terms in pData for
- ** sorting.
- */
- nTerms = fts2HashCount(pTerms);
- assert( nTerms>0 );
- pData = sqlite3_malloc(nTerms*sizeof(TermData));
- if( pData==NULL ) return SQLITE_NOMEM;
-
- nResultBytes = 0;
- for(iTerm = 0, e = fts2HashFirst(pTerms); e; iTerm++, e = fts2HashNext(e)){
- nResultBytes += fts2HashKeysize(e)+1; /* Term plus trailing space */
- assert( iTerm0 ); /* nTerms>0, nResultsBytes must be, too. */
- result = sqlite3_malloc(nResultBytes);
- if( result==NULL ){
- sqlite3_free(pData);
- return SQLITE_NOMEM;
- }
-
- if( nTerms>1 ) qsort(pData, nTerms, sizeof(*pData), termDataCmp);
-
- /* Read the terms in order to build the result. */
- iByte = 0;
- for(iTerm=0; iTerm0 ){
- rc = generateTermsResult(pContext, &terms);
- if( rc==SQLITE_NOMEM ){
- generateError(pContext, "dump_terms", "out of memory");
- }else{
- assert( rc==SQLITE_OK );
- }
- }else if( argc==3 ){
- /* The specific segment asked for could not be found. */
- generateError(pContext, "dump_terms", "segment not found");
- }else{
- /* No segments found. */
- /* TODO(shess): It should be impossible to reach this. This
- ** case can only happen for an empty table, in which case
- ** SQLite has no rows to call this function on.
- */
- sqlite3_result_null(pContext);
- }
- }
- sqlite3Fts2HashClear(&terms);
- }
-}
-
-/* Expand the DL_DEFAULT doclist in pData into a text result in
-** pContext.
-*/
-static void createDoclistResult(sqlite3_context *pContext,
- const char *pData, int nData){
- DataBuffer dump;
- DLReader dlReader;
-
- assert( pData!=NULL && nData>0 );
-
- dataBufferInit(&dump, 0);
- dlrInit(&dlReader, DL_DEFAULT, pData, nData);
- for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){
- char buf[256];
- PLReader plReader;
-
- plrInit(&plReader, &dlReader);
- if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){
- sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader));
- dataBufferAppend(&dump, buf, strlen(buf));
- }else{
- int iColumn = plrColumn(&plReader);
-
- sqlite3_snprintf(sizeof(buf), buf, "[%lld %d[",
- dlrDocid(&dlReader), iColumn);
- dataBufferAppend(&dump, buf, strlen(buf));
-
- for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){
- if( plrColumn(&plReader)!=iColumn ){
- iColumn = plrColumn(&plReader);
- sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn);
- assert( dump.nData>0 );
- dump.nData--; /* Overwrite trailing space. */
- assert( dump.pData[dump.nData]==' ');
- dataBufferAppend(&dump, buf, strlen(buf));
- }
- if( DL_DEFAULT==DL_POSITIONS_OFFSETS ){
- sqlite3_snprintf(sizeof(buf), buf, "%d,%d,%d ",
- plrPosition(&plReader),
- plrStartOffset(&plReader), plrEndOffset(&plReader));
- }else if( DL_DEFAULT==DL_POSITIONS ){
- sqlite3_snprintf(sizeof(buf), buf, "%d ", plrPosition(&plReader));
- }else{
- assert( NULL=="Unhandled DL_DEFAULT value");
- }
- dataBufferAppend(&dump, buf, strlen(buf));
- }
- plrDestroy(&plReader);
-
- assert( dump.nData>0 );
- dump.nData--; /* Overwrite trailing space. */
- assert( dump.pData[dump.nData]==' ');
- dataBufferAppend(&dump, "]] ", 3);
- }
- }
- dlrDestroy(&dlReader);
-
- assert( dump.nData>0 );
- dump.nData--; /* Overwrite trailing space. */
- assert( dump.pData[dump.nData]==' ');
- dump.pData[dump.nData] = '\0';
- assert( dump.nData>0 );
-
- /* Passes ownership of dump's buffer to pContext. */
- sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free);
- dump.pData = NULL;
- dump.nData = dump.nCapacity = 0;
-}
-
-/* Implements dump_doclist() for use in inspecting the fts2 index from
-** tests. TEXT result containing a string representation of the
-** doclist for the indicated term. dump_doclist(t, term, level, idx)
-** dumps the doclist for term from the segment specified by level, idx
-** (in %_segdir), while dump_doclist(t, term) dumps the logical
-** doclist for the term across all segments. The per-segment doclist
-** can contain deletions, while the full-index doclist will not
-** (deletions are omitted).
-**
-** Result formats differ with the setting of DL_DEFAULTS. Examples:
-**
-** DL_DOCIDS: [1] [3] [7]
-** DL_POSITIONS: [1 0[0 4] 1[17]] [3 1[5]]
-** DL_POSITIONS_OFFSETS: [1 0[0,0,3 4,23,26] 1[17,102,105]] [3 1[5,20,23]]
-**
-** In each case the number after the outer '[' is the docid. In the
-** latter two cases, the number before the inner '[' is the column
-** associated with the values within. For DL_POSITIONS the numbers
-** within are the positions, for DL_POSITIONS_OFFSETS they are the
-** position, the start offset, and the end offset.
-*/
-static void dumpDoclistFunc(
- sqlite3_context *pContext,
- int argc, sqlite3_value **argv
-){
- fulltext_cursor *pCursor;
- if( argc!=2 && argc!=4 ){
- generateError(pContext, "dump_doclist", "incorrect arguments");
- }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ||
- sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){
- generateError(pContext, "dump_doclist", "illegal first argument");
- }else if( sqlite3_value_text(argv[1])==NULL ||
- sqlite3_value_text(argv[1])[0]=='\0' ){
- generateError(pContext, "dump_doclist", "empty second argument");
- }else{
- const char *pTerm = (const char *)sqlite3_value_text(argv[1]);
- const int nTerm = strlen(pTerm);
- fulltext_vtab *v;
- int rc;
- DataBuffer doclist;
-
- memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor));
- v = cursor_vtab(pCursor);
-
- dataBufferInit(&doclist, 0);
-
- /* termSelect() yields the same logical doclist that queries are
- ** run against.
- */
- if( argc==2 ){
- rc = termSelect(v, v->nColumn, pTerm, nTerm, 0, DL_DEFAULT, &doclist);
- }else{
- sqlite3_stmt *s = NULL;
-
- /* Get our specific segment's information. */
- rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s);
- if( rc==SQLITE_OK ){
- rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[2]));
- if( rc==SQLITE_OK ){
- rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[3]));
- }
- }
-
- if( rc==SQLITE_OK ){
- rc = sqlite3_step(s);
-
- if( rc==SQLITE_DONE ){
- dataBufferDestroy(&doclist);
- generateError(pContext, "dump_doclist", "segment not found");
- return;
- }
-
- /* Found a segment, load it into doclist. */
- if( rc==SQLITE_ROW ){
- const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1);
- const char *pData = sqlite3_column_blob(s, 2);
- const int nData = sqlite3_column_bytes(s, 2);
-
- /* loadSegment() is used by termSelect() to load each
- ** segment's data.
- */
- rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, 0,
- &doclist);
- if( rc==SQLITE_OK ){
- rc = sqlite3_step(s);
-
- /* Should not have more than one matching segment. */
- if( rc!=SQLITE_DONE ){
- sqlite3_reset(s);
- dataBufferDestroy(&doclist);
- generateError(pContext, "dump_doclist", "invalid segdir");
- return;
- }
- rc = SQLITE_OK;
- }
- }
- }
-
- sqlite3_reset(s);
- }
-
- if( rc==SQLITE_OK ){
- if( doclist.nData>0 ){
- createDoclistResult(pContext, doclist.pData, doclist.nData);
- }else{
- /* TODO(shess): This can happen if the term is not present, or
- ** if all instances of the term have been deleted and this is
- ** an all-index dump. It may be interesting to distinguish
- ** these cases.
- */
- sqlite3_result_text(pContext, "", 0, SQLITE_STATIC);
- }
- }else if( rc==SQLITE_NOMEM ){
- /* Handle out-of-memory cases specially because if they are
- ** generated in fts2 code they may not be reflected in the db
- ** handle.
- */
- /* TODO(shess): Handle this more comprehensively.
- ** sqlite3ErrStr() has what I need, but is internal.
- */
- generateError(pContext, "dump_doclist", "out of memory");
- }else{
- generateError(pContext, "dump_doclist", NULL);
- }
-
- dataBufferDestroy(&doclist);
- }
-}
-#endif
-
-/*
-** This routine implements the xFindFunction method for the FTS2
-** virtual table.
-*/
-static int fulltextFindFunction(
- sqlite3_vtab *pVtab,
- int nArg,
- const char *zName,
- void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
- void **ppArg
-){
- if( strcmp(zName,"snippet")==0 ){
- *pxFunc = snippetFunc;
- return 1;
- }else if( strcmp(zName,"offsets")==0 ){
- *pxFunc = snippetOffsetsFunc;
- return 1;
- }else if( strcmp(zName,"optimize")==0 ){
- *pxFunc = optimizeFunc;
- return 1;
-#ifdef SQLITE_TEST
- /* NOTE(shess): These functions are present only for testing
- ** purposes. No particular effort is made to optimize their
- ** execution or how they build their results.
- */
- }else if( strcmp(zName,"dump_terms")==0 ){
- /* fprintf(stderr, "Found dump_terms\n"); */
- *pxFunc = dumpTermsFunc;
- return 1;
- }else if( strcmp(zName,"dump_doclist")==0 ){
- /* fprintf(stderr, "Found dump_doclist\n"); */
- *pxFunc = dumpDoclistFunc;
- return 1;
-#endif
- }
- return 0;
-}
-
-/*
-** Rename an fts2 table.
-*/
-static int fulltextRename(
- sqlite3_vtab *pVtab,
- const char *zName
-){
- fulltext_vtab *p = (fulltext_vtab *)pVtab;
- int rc = SQLITE_NOMEM;
- char *zSql = sqlite3_mprintf(
- "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';"
- "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';"
- "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';"
- , p->zDb, p->zName, zName
- , p->zDb, p->zName, zName
- , p->zDb, p->zName, zName
- );
- if( zSql ){
- rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
- sqlite3_free(zSql);
- }
- return rc;
-}
-
-static const sqlite3_module fts2Module = {
- /* iVersion */ 0,
- /* xCreate */ fulltextCreate,
- /* xConnect */ fulltextConnect,
- /* xBestIndex */ fulltextBestIndex,
- /* xDisconnect */ fulltextDisconnect,
- /* xDestroy */ fulltextDestroy,
- /* xOpen */ fulltextOpen,
- /* xClose */ fulltextClose,
- /* xFilter */ fulltextFilter,
- /* xNext */ fulltextNext,
- /* xEof */ fulltextEof,
- /* xColumn */ fulltextColumn,
- /* xRowid */ fulltextRowid,
- /* xUpdate */ fulltextUpdate,
- /* xBegin */ fulltextBegin,
- /* xSync */ fulltextSync,
- /* xCommit */ fulltextCommit,
- /* xRollback */ fulltextRollback,
- /* xFindFunction */ fulltextFindFunction,
- /* xRename */ fulltextRename,
-};
-
-static void hashDestroy(void *p){
- fts2Hash *pHash = (fts2Hash *)p;
- sqlite3Fts2HashClear(pHash);
- sqlite3_free(pHash);
-}
-
-/*
-** The fts2 built-in tokenizers - "simple" and "porter" - are implemented
-** in files fts2_tokenizer1.c and fts2_porter.c respectively. The following
-** two forward declarations are for functions declared in these files
-** used to retrieve the respective implementations.
-**
-** Calling sqlite3Fts2SimpleTokenizerModule() sets the value pointed
-** to by the argument to point a the "simple" tokenizer implementation.
-** Function ...PorterTokenizerModule() sets *pModule to point to the
-** porter tokenizer/stemmer implementation.
-*/
-void sqlite3Fts2SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-void sqlite3Fts2PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-void sqlite3Fts2IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-
-int sqlite3Fts2InitHashTable(sqlite3 *, fts2Hash *, const char *);
-
-/*
-** Initialize the fts2 extension. If this extension is built as part
-** of the sqlite library, then this function is called directly by
-** SQLite. If fts2 is built as a dynamically loadable extension, this
-** function is called by the sqlite3_extension_init() entry point.
-*/
-int sqlite3Fts2Init(sqlite3 *db){
- int rc = SQLITE_OK;
- fts2Hash *pHash = 0;
- const sqlite3_tokenizer_module *pSimple = 0;
- const sqlite3_tokenizer_module *pPorter = 0;
- const sqlite3_tokenizer_module *pIcu = 0;
-
- sqlite3Fts2SimpleTokenizerModule(&pSimple);
- sqlite3Fts2PorterTokenizerModule(&pPorter);
-#ifdef SQLITE_ENABLE_ICU
- sqlite3Fts2IcuTokenizerModule(&pIcu);
-#endif
-
- /* Allocate and initialize the hash-table used to store tokenizers. */
- pHash = sqlite3_malloc(sizeof(fts2Hash));
- if( !pHash ){
- rc = SQLITE_NOMEM;
- }else{
- sqlite3Fts2HashInit(pHash, FTS2_HASH_STRING, 1);
- }
-
- /* Load the built-in tokenizers into the hash table */
- if( rc==SQLITE_OK ){
- if( sqlite3Fts2HashInsert(pHash, "simple", 7, (void *)pSimple)
- || sqlite3Fts2HashInsert(pHash, "porter", 7, (void *)pPorter)
- || (pIcu && sqlite3Fts2HashInsert(pHash, "icu", 4, (void *)pIcu))
- ){
- rc = SQLITE_NOMEM;
- }
- }
-
- /* Create the virtual table wrapper around the hash-table and overload
- ** the two scalar functions. If this is successful, register the
- ** module with sqlite.
- */
- if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3Fts2InitHashTable(db, pHash, "fts2_tokenizer"))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1))
-#ifdef SQLITE_TEST
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_terms", -1))
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_doclist", -1))
-#endif
- ){
- return sqlite3_create_module_v2(
- db, "fts2", &fts2Module, (void *)pHash, hashDestroy
- );
- }
-
- /* An error has occurred. Delete the hash table and return the error code. */
- assert( rc!=SQLITE_OK );
- if( pHash ){
- sqlite3Fts2HashClear(pHash);
- sqlite3_free(pHash);
- }
- return rc;
-}
-
-#if !SQLITE_CORE
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-int sqlite3_fts2_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- SQLITE_EXTENSION_INIT2(pApi)
- return sqlite3Fts2Init(db);
-}
-#endif
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
diff --git a/sqlite/ext/fts2/fts2.h b/sqlite/ext/fts2/fts2.h
deleted file mode 100644
index 4da4c387..00000000
--- a/sqlite/ext/fts2/fts2.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-** 2006 Oct 10
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This header file is used by programs that want to link against the
-** FTS2 library. All it does is declare the sqlite3Fts2Init() interface.
-*/
-#include "sqlite3.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-int sqlite3Fts2Init(sqlite3 *db);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif /* __cplusplus */
diff --git a/sqlite/ext/fts2/fts2_hash.c b/sqlite/ext/fts2/fts2_hash.c
deleted file mode 100644
index 3596dcf0..00000000
--- a/sqlite/ext/fts2/fts2_hash.c
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the implementation of generic hash-tables used in SQLite.
-** We've modified it slightly to serve as a standalone hash table
-** implementation for the full-text indexing module.
-*/
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS2 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS2 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
-
-#include
-#include
-#include
-
-#include "sqlite3.h"
-#include "sqlite3ext.h"
-SQLITE_EXTENSION_INIT3
-#include "fts2_hash.h"
-
-/*
-** Malloc and Free functions
-*/
-static void *fts2HashMalloc(int n){
- void *p = sqlite3_malloc(n);
- if( p ){
- memset(p, 0, n);
- }
- return p;
-}
-static void fts2HashFree(void *p){
- sqlite3_free(p);
-}
-
-/* Turn bulk memory into a hash table object by initializing the
-** fields of the Hash structure.
-**
-** "pNew" is a pointer to the hash table that is to be initialized.
-** keyClass is one of the constants
-** FTS2_HASH_BINARY or FTS2_HASH_STRING. The value of keyClass
-** determines what kind of key the hash table will use. "copyKey" is
-** true if the hash table should make its own private copy of keys and
-** false if it should just use the supplied pointer.
-*/
-void sqlite3Fts2HashInit(fts2Hash *pNew, int keyClass, int copyKey){
- assert( pNew!=0 );
- assert( keyClass>=FTS2_HASH_STRING && keyClass<=FTS2_HASH_BINARY );
- pNew->keyClass = keyClass;
- pNew->copyKey = copyKey;
- pNew->first = 0;
- pNew->count = 0;
- pNew->htsize = 0;
- pNew->ht = 0;
-}
-
-/* Remove all entries from a hash table. Reclaim all memory.
-** Call this routine to delete a hash table or to reset a hash table
-** to the empty state.
-*/
-void sqlite3Fts2HashClear(fts2Hash *pH){
- fts2HashElem *elem; /* For looping over all elements of the table */
-
- assert( pH!=0 );
- elem = pH->first;
- pH->first = 0;
- fts2HashFree(pH->ht);
- pH->ht = 0;
- pH->htsize = 0;
- while( elem ){
- fts2HashElem *next_elem = elem->next;
- if( pH->copyKey && elem->pKey ){
- fts2HashFree(elem->pKey);
- }
- fts2HashFree(elem);
- elem = next_elem;
- }
- pH->count = 0;
-}
-
-/*
-** Hash and comparison functions when the mode is FTS2_HASH_STRING
-*/
-static int strHash(const void *pKey, int nKey){
- const char *z = (const char *)pKey;
- int h = 0;
- if( nKey<=0 ) nKey = (int) strlen(z);
- while( nKey > 0 ){
- h = (h<<3) ^ h ^ *z++;
- nKey--;
- }
- return h & 0x7fffffff;
-}
-static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( n1!=n2 ) return 1;
- return strncmp((const char*)pKey1,(const char*)pKey2,n1);
-}
-
-/*
-** Hash and comparison functions when the mode is FTS2_HASH_BINARY
-*/
-static int binHash(const void *pKey, int nKey){
- int h = 0;
- const char *z = (const char *)pKey;
- while( nKey-- > 0 ){
- h = (h<<3) ^ h ^ *(z++);
- }
- return h & 0x7fffffff;
-}
-static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
- if( n1!=n2 ) return 1;
- return memcmp(pKey1,pKey2,n1);
-}
-
-/*
-** Return a pointer to the appropriate hash function given the key class.
-**
-** The C syntax in this function definition may be unfamilar to some
-** programmers, so we provide the following additional explanation:
-**
-** The name of the function is "hashFunction". The function takes a
-** single parameter "keyClass". The return value of hashFunction()
-** is a pointer to another function. Specifically, the return value
-** of hashFunction() is a pointer to a function that takes two parameters
-** with types "const void*" and "int" and returns an "int".
-*/
-static int (*hashFunction(int keyClass))(const void*,int){
- if( keyClass==FTS2_HASH_STRING ){
- return &strHash;
- }else{
- assert( keyClass==FTS2_HASH_BINARY );
- return &binHash;
- }
-}
-
-/*
-** Return a pointer to the appropriate hash function given the key class.
-**
-** For help in interpreted the obscure C code in the function definition,
-** see the header comment on the previous function.
-*/
-static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
- if( keyClass==FTS2_HASH_STRING ){
- return &strCompare;
- }else{
- assert( keyClass==FTS2_HASH_BINARY );
- return &binCompare;
- }
-}
-
-/* Link an element into the hash table
-*/
-static void insertElement(
- fts2Hash *pH, /* The complete hash table */
- struct _fts2ht *pEntry, /* The entry into which pNew is inserted */
- fts2HashElem *pNew /* The element to be inserted */
-){
- fts2HashElem *pHead; /* First element already in pEntry */
- pHead = pEntry->chain;
- if( pHead ){
- pNew->next = pHead;
- pNew->prev = pHead->prev;
- if( pHead->prev ){ pHead->prev->next = pNew; }
- else { pH->first = pNew; }
- pHead->prev = pNew;
- }else{
- pNew->next = pH->first;
- if( pH->first ){ pH->first->prev = pNew; }
- pNew->prev = 0;
- pH->first = pNew;
- }
- pEntry->count++;
- pEntry->chain = pNew;
-}
-
-
-/* Resize the hash table so that it cantains "new_size" buckets.
-** "new_size" must be a power of 2. The hash table might fail
-** to resize if sqliteMalloc() fails.
-*/
-static void rehash(fts2Hash *pH, int new_size){
- struct _fts2ht *new_ht; /* The new hash table */
- fts2HashElem *elem, *next_elem; /* For looping over existing elements */
- int (*xHash)(const void*,int); /* The hash function */
-
- assert( (new_size & (new_size-1))==0 );
- new_ht = (struct _fts2ht *)fts2HashMalloc( new_size*sizeof(struct _fts2ht) );
- if( new_ht==0 ) return;
- fts2HashFree(pH->ht);
- pH->ht = new_ht;
- pH->htsize = new_size;
- xHash = hashFunction(pH->keyClass);
- for(elem=pH->first, pH->first=0; elem; elem = next_elem){
- int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
- next_elem = elem->next;
- insertElement(pH, &new_ht[h], elem);
- }
-}
-
-/* This function (for internal use only) locates an element in an
-** hash table that matches the given key. The hash for this key has
-** already been computed and is passed as the 4th parameter.
-*/
-static fts2HashElem *findElementGivenHash(
- const fts2Hash *pH, /* The pH to be searched */
- const void *pKey, /* The key we are searching for */
- int nKey,
- int h /* The hash for this key. */
-){
- fts2HashElem *elem; /* Used to loop thru the element list */
- int count; /* Number of elements left to test */
- int (*xCompare)(const void*,int,const void*,int); /* comparison function */
-
- if( pH->ht ){
- struct _fts2ht *pEntry = &pH->ht[h];
- elem = pEntry->chain;
- count = pEntry->count;
- xCompare = compareFunction(pH->keyClass);
- while( count-- && elem ){
- if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
- return elem;
- }
- elem = elem->next;
- }
- }
- return 0;
-}
-
-/* Remove a single entry from the hash table given a pointer to that
-** element and a hash on the element's key.
-*/
-static void removeElementGivenHash(
- fts2Hash *pH, /* The pH containing "elem" */
- fts2HashElem* elem, /* The element to be removed from the pH */
- int h /* Hash value for the element */
-){
- struct _fts2ht *pEntry;
- if( elem->prev ){
- elem->prev->next = elem->next;
- }else{
- pH->first = elem->next;
- }
- if( elem->next ){
- elem->next->prev = elem->prev;
- }
- pEntry = &pH->ht[h];
- if( pEntry->chain==elem ){
- pEntry->chain = elem->next;
- }
- pEntry->count--;
- if( pEntry->count<=0 ){
- pEntry->chain = 0;
- }
- if( pH->copyKey && elem->pKey ){
- fts2HashFree(elem->pKey);
- }
- fts2HashFree( elem );
- pH->count--;
- if( pH->count<=0 ){
- assert( pH->first==0 );
- assert( pH->count==0 );
- fts2HashClear(pH);
- }
-}
-
-/* Attempt to locate an element of the hash table pH with a key
-** that matches pKey,nKey. Return the data for this element if it is
-** found, or NULL if there is no match.
-*/
-void *sqlite3Fts2HashFind(const fts2Hash *pH, const void *pKey, int nKey){
- int h; /* A hash on key */
- fts2HashElem *elem; /* The element that matches key */
- int (*xHash)(const void*,int); /* The hash function */
-
- if( pH==0 || pH->ht==0 ) return 0;
- xHash = hashFunction(pH->keyClass);
- assert( xHash!=0 );
- h = (*xHash)(pKey,nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
- return elem ? elem->data : 0;
-}
-
-/* Insert an element into the hash table pH. The key is pKey,nKey
-** and the data is "data".
-**
-** If no element exists with a matching key, then a new
-** element is created. A copy of the key is made if the copyKey
-** flag is set. NULL is returned.
-**
-** If another element already exists with the same key, then the
-** new data replaces the old data and the old data is returned.
-** The key is not copied in this instance. If a malloc fails, then
-** the new data is returned and the hash table is unchanged.
-**
-** If the "data" parameter to this function is NULL, then the
-** element corresponding to "key" is removed from the hash table.
-*/
-void *sqlite3Fts2HashInsert(
- fts2Hash *pH, /* The hash table to insert into */
- const void *pKey, /* The key */
- int nKey, /* Number of bytes in the key */
- void *data /* The data */
-){
- int hraw; /* Raw hash value of the key */
- int h; /* the hash of the key modulo hash table size */
- fts2HashElem *elem; /* Used to loop thru the element list */
- fts2HashElem *new_elem; /* New element added to the pH */
- int (*xHash)(const void*,int); /* The hash function */
-
- assert( pH!=0 );
- xHash = hashFunction(pH->keyClass);
- assert( xHash!=0 );
- hraw = (*xHash)(pKey, nKey);
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- elem = findElementGivenHash(pH,pKey,nKey,h);
- if( elem ){
- void *old_data = elem->data;
- if( data==0 ){
- removeElementGivenHash(pH,elem,h);
- }else{
- elem->data = data;
- }
- return old_data;
- }
- if( data==0 ) return 0;
- new_elem = (fts2HashElem*)fts2HashMalloc( sizeof(fts2HashElem) );
- if( new_elem==0 ) return data;
- if( pH->copyKey && pKey!=0 ){
- new_elem->pKey = fts2HashMalloc( nKey );
- if( new_elem->pKey==0 ){
- fts2HashFree(new_elem);
- return data;
- }
- memcpy((void*)new_elem->pKey, pKey, nKey);
- }else{
- new_elem->pKey = (void*)pKey;
- }
- new_elem->nKey = nKey;
- pH->count++;
- if( pH->htsize==0 ){
- rehash(pH,8);
- if( pH->htsize==0 ){
- pH->count = 0;
- fts2HashFree(new_elem);
- return data;
- }
- }
- if( pH->count > pH->htsize ){
- rehash(pH,pH->htsize*2);
- }
- assert( pH->htsize>0 );
- assert( (pH->htsize & (pH->htsize-1))==0 );
- h = hraw & (pH->htsize-1);
- insertElement(pH, &pH->ht[h], new_elem);
- new_elem->data = data;
- return 0;
-}
-
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
diff --git a/sqlite/ext/fts2/fts2_hash.h b/sqlite/ext/fts2/fts2_hash.h
deleted file mode 100644
index 02936f18..00000000
--- a/sqlite/ext/fts2/fts2_hash.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the header file for the generic hash-table implementation
-** used in SQLite. We've modified it slightly to serve as a standalone
-** hash table implementation for the full-text indexing module.
-**
-*/
-#ifndef _FTS2_HASH_H_
-#define _FTS2_HASH_H_
-
-/* Forward declarations of structures. */
-typedef struct fts2Hash fts2Hash;
-typedef struct fts2HashElem fts2HashElem;
-
-/* A complete hash table is an instance of the following structure.
-** The internals of this structure are intended to be opaque -- client
-** code should not attempt to access or modify the fields of this structure
-** directly. Change this structure only by using the routines below.
-** However, many of the "procedures" and "functions" for modifying and
-** accessing this structure are really macros, so we can't really make
-** this structure opaque.
-*/
-struct fts2Hash {
- char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
- char copyKey; /* True if copy of key made on insert */
- int count; /* Number of entries in this table */
- fts2HashElem *first; /* The first element of the array */
- int htsize; /* Number of buckets in the hash table */
- struct _fts2ht { /* the hash table */
- int count; /* Number of entries with this hash */
- fts2HashElem *chain; /* Pointer to first entry with this hash */
- } *ht;
-};
-
-/* Each element in the hash table is an instance of the following
-** structure. All elements are stored on a single doubly-linked list.
-**
-** Again, this structure is intended to be opaque, but it can't really
-** be opaque because it is used by macros.
-*/
-struct fts2HashElem {
- fts2HashElem *next, *prev; /* Next and previous elements in the table */
- void *data; /* Data associated with this element */
- void *pKey; int nKey; /* Key associated with this element */
-};
-
-/*
-** There are 2 different modes of operation for a hash table:
-**
-** FTS2_HASH_STRING pKey points to a string that is nKey bytes long
-** (including the null-terminator, if any). Case
-** is respected in comparisons.
-**
-** FTS2_HASH_BINARY pKey points to binary data nKey bytes long.
-** memcmp() is used to compare keys.
-**
-** A copy of the key is made if the copyKey parameter to fts2HashInit is 1.
-*/
-#define FTS2_HASH_STRING 1
-#define FTS2_HASH_BINARY 2
-
-/*
-** Access routines. To delete, insert a NULL pointer.
-*/
-void sqlite3Fts2HashInit(fts2Hash*, int keytype, int copyKey);
-void *sqlite3Fts2HashInsert(fts2Hash*, const void *pKey, int nKey, void *pData);
-void *sqlite3Fts2HashFind(const fts2Hash*, const void *pKey, int nKey);
-void sqlite3Fts2HashClear(fts2Hash*);
-
-/*
-** Shorthand for the functions above
-*/
-#define fts2HashInit sqlite3Fts2HashInit
-#define fts2HashInsert sqlite3Fts2HashInsert
-#define fts2HashFind sqlite3Fts2HashFind
-#define fts2HashClear sqlite3Fts2HashClear
-
-/*
-** Macros for looping over all elements of a hash table. The idiom is
-** like this:
-**
-** fts2Hash h;
-** fts2HashElem *p;
-** ...
-** for(p=fts2HashFirst(&h); p; p=fts2HashNext(p)){
-** SomeStructure *pData = fts2HashData(p);
-** // do something with pData
-** }
-*/
-#define fts2HashFirst(H) ((H)->first)
-#define fts2HashNext(E) ((E)->next)
-#define fts2HashData(E) ((E)->data)
-#define fts2HashKey(E) ((E)->pKey)
-#define fts2HashKeysize(E) ((E)->nKey)
-
-/*
-** Number of entries in a hash table
-*/
-#define fts2HashCount(H) ((H)->count)
-
-#endif /* _FTS2_HASH_H_ */
diff --git a/sqlite/ext/fts2/fts2_icu.c b/sqlite/ext/fts2/fts2_icu.c
deleted file mode 100644
index 2670301f..00000000
--- a/sqlite/ext/fts2/fts2_icu.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
-** 2007 June 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This file implements a tokenizer for fts2 based on the ICU library.
-**
-** $Id: fts2_icu.c,v 1.3 2008/12/18 05:30:26 danielk1977 Exp $
-*/
-
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
-#ifdef SQLITE_ENABLE_ICU
-
-#include
-#include
-#include "fts2_tokenizer.h"
-
-#include
-#include
-#include
-#include
-
-typedef struct IcuTokenizer IcuTokenizer;
-typedef struct IcuCursor IcuCursor;
-
-struct IcuTokenizer {
- sqlite3_tokenizer base;
- char *zLocale;
-};
-
-struct IcuCursor {
- sqlite3_tokenizer_cursor base;
-
- UBreakIterator *pIter; /* ICU break-iterator object */
- int nChar; /* Number of UChar elements in pInput */
- UChar *aChar; /* Copy of input using utf-16 encoding */
- int *aOffset; /* Offsets of each character in utf-8 input */
-
- int nBuffer;
- char *zBuffer;
-
- int iToken;
-};
-
-/*
-** Create a new tokenizer instance.
-*/
-static int icuCreate(
- int argc, /* Number of entries in argv[] */
- const char * const *argv, /* Tokenizer creation arguments */
- sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
-){
- IcuTokenizer *p;
- int n = 0;
-
- if( argc>0 ){
- n = strlen(argv[0])+1;
- }
- p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n);
- if( !p ){
- return SQLITE_NOMEM;
- }
- memset(p, 0, sizeof(IcuTokenizer));
-
- if( n ){
- p->zLocale = (char *)&p[1];
- memcpy(p->zLocale, argv[0], n);
- }
-
- *ppTokenizer = (sqlite3_tokenizer *)p;
-
- return SQLITE_OK;
-}
-
-/*
-** Destroy a tokenizer
-*/
-static int icuDestroy(sqlite3_tokenizer *pTokenizer){
- IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
- sqlite3_free(p);
- return SQLITE_OK;
-}
-
-/*
-** Prepare to begin tokenizing a particular string. The input
-** string to be tokenized is pInput[0..nBytes-1]. A cursor
-** used to incrementally tokenize this string is returned in
-** *ppCursor.
-*/
-static int icuOpen(
- sqlite3_tokenizer *pTokenizer, /* The tokenizer */
- const char *zInput, /* Input string */
- int nInput, /* Length of zInput in bytes */
- sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
-){
- IcuTokenizer *p = (IcuTokenizer *)pTokenizer;
- IcuCursor *pCsr;
-
- const int32_t opt = U_FOLD_CASE_DEFAULT;
- UErrorCode status = U_ZERO_ERROR;
- int nChar;
-
- UChar32 c;
- int iInput = 0;
- int iOut = 0;
-
- *ppCursor = 0;
-
- if( nInput<0 ){
- nInput = strlen(zInput);
- }
- nChar = nInput+1;
- pCsr = (IcuCursor *)sqlite3_malloc(
- sizeof(IcuCursor) + /* IcuCursor */
- ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */
- (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
- );
- if( !pCsr ){
- return SQLITE_NOMEM;
- }
- memset(pCsr, 0, sizeof(IcuCursor));
- pCsr->aChar = (UChar *)&pCsr[1];
- pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3];
-
- pCsr->aOffset[iOut] = iInput;
- U8_NEXT(zInput, iInput, nInput, c);
- while( c>0 ){
- int isError = 0;
- c = u_foldCase(c, opt);
- U16_APPEND(pCsr->aChar, iOut, nChar, c, isError);
- if( isError ){
- sqlite3_free(pCsr);
- return SQLITE_ERROR;
- }
- pCsr->aOffset[iOut] = iInput;
-
- if( iInputpIter = ubrk_open(UBRK_WORD, p->zLocale, pCsr->aChar, iOut, &status);
- if( !U_SUCCESS(status) ){
- sqlite3_free(pCsr);
- return SQLITE_ERROR;
- }
- pCsr->nChar = iOut;
-
- ubrk_first(pCsr->pIter);
- *ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
- return SQLITE_OK;
-}
-
-/*
-** Close a tokenization cursor previously opened by a call to icuOpen().
-*/
-static int icuClose(sqlite3_tokenizer_cursor *pCursor){
- IcuCursor *pCsr = (IcuCursor *)pCursor;
- ubrk_close(pCsr->pIter);
- sqlite3_free(pCsr->zBuffer);
- sqlite3_free(pCsr);
- return SQLITE_OK;
-}
-
-/*
-** Extract the next token from a tokenization cursor.
-*/
-static int icuNext(
- sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */
- const char **ppToken, /* OUT: *ppToken is the token text */
- int *pnBytes, /* OUT: Number of bytes in token */
- int *piStartOffset, /* OUT: Starting offset of token */
- int *piEndOffset, /* OUT: Ending offset of token */
- int *piPosition /* OUT: Position integer of token */
-){
- IcuCursor *pCsr = (IcuCursor *)pCursor;
-
- int iStart = 0;
- int iEnd = 0;
- int nByte = 0;
-
- while( iStart==iEnd ){
- UChar32 c;
-
- iStart = ubrk_current(pCsr->pIter);
- iEnd = ubrk_next(pCsr->pIter);
- if( iEnd==UBRK_DONE ){
- return SQLITE_DONE;
- }
-
- while( iStartaChar, iWhite, pCsr->nChar, c);
- if( u_isspace(c) ){
- iStart = iWhite;
- }else{
- break;
- }
- }
- assert(iStart<=iEnd);
- }
-
- do {
- UErrorCode status = U_ZERO_ERROR;
- if( nByte ){
- char *zNew = sqlite3_realloc(pCsr->zBuffer, nByte);
- if( !zNew ){
- return SQLITE_NOMEM;
- }
- pCsr->zBuffer = zNew;
- pCsr->nBuffer = nByte;
- }
-
- u_strToUTF8(
- pCsr->zBuffer, pCsr->nBuffer, &nByte, /* Output vars */
- &pCsr->aChar[iStart], iEnd-iStart, /* Input vars */
- &status /* Output success/failure */
- );
- } while( nByte>pCsr->nBuffer );
-
- *ppToken = pCsr->zBuffer;
- *pnBytes = nByte;
- *piStartOffset = pCsr->aOffset[iStart];
- *piEndOffset = pCsr->aOffset[iEnd];
- *piPosition = pCsr->iToken++;
-
- return SQLITE_OK;
-}
-
-/*
-** The set of routines that implement the simple tokenizer
-*/
-static const sqlite3_tokenizer_module icuTokenizerModule = {
- 0, /* iVersion */
- icuCreate, /* xCreate */
- icuDestroy, /* xCreate */
- icuOpen, /* xOpen */
- icuClose, /* xClose */
- icuNext, /* xNext */
-};
-
-/*
-** Set *ppModule to point at the implementation of the ICU tokenizer.
-*/
-void sqlite3Fts2IcuTokenizerModule(
- sqlite3_tokenizer_module const**ppModule
-){
- *ppModule = &icuTokenizerModule;
-}
-
-#endif /* defined(SQLITE_ENABLE_ICU) */
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) */
diff --git a/sqlite/ext/fts2/fts2_porter.c b/sqlite/ext/fts2/fts2_porter.c
deleted file mode 100644
index 881baf71..00000000
--- a/sqlite/ext/fts2/fts2_porter.c
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
-** 2006 September 30
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** Implementation of the full-text-search tokenizer that implements
-** a Porter stemmer.
-*/
-
-/*
-** The code in this file is only compiled if:
-**
-** * The FTS2 module is being built as an extension
-** (in which case SQLITE_CORE is not defined), or
-**
-** * The FTS2 module is being built into the core of
-** SQLite (in which case SQLITE_ENABLE_FTS2 is defined).
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2)
-
-
-#include
-#include
-#include
-#include